watchメソッドでオブジェクトが存在するようになるのを待ってから実行する

はじめwatchを全部waitと勘違いして書いてました。id:os0xさんご指摘ありがとうございました。

watchはIEにはないのでとりあえずFirefoxのはなしです。いつも断りなくFirefox前提で書いてますけど。

以前にscriptタグを動的に生成してページがレンダリングされてからjavascriptを読み込ませることで重たい処理をしていたりするjavascriptによるストレスを軽減するような細工をしたことがありました。

そのときに大変だったのは、動的にロードすると書いた順番でスクリプトのロードが完了するとは限らないので、依存関係のあるスクリプト間で同期をとる必要があったことでした。そのときはsetIntervalでポーリングして必要とするオブジェクトがそろったら実行、みたいなことを書きましたがFirefoxだとwatchを使ってスマートに書くことができます。

AutoPagerizeとMinibufferとLDRizeのロード順序をどうすればいいかわからない、みたいなのも、これで解決!てきとうに書いても大丈夫なようにできるよ、と思ったけどそこはGreasemonkeyのsandboxのXPCNativeWrapperの制約でwindow.watchが利用できなくてだめで、意気消沈してこのエントリもお蔵入りしてたのですがwatchつかうと便利なときがあるっていう意味で書いておきます。

MinibufferでAutoPagerizeを参照したいときは


if ( window.AutoPagerize ) {
    // do something
} else {
  window.watch( "AutoPagerize", function (name, oldValue, newValue) {
      // do something
      return newValue;
  } );
}

としておけばポーリングするオーバーヘッドで損することなくAutoPagerizeが設定されたときに do something することができます。実際のGreasemonkeyではwindow.watchがNGなのでだめなんですけどねー。

さらにほんとはwindow.AutoPagerizeが設定されるタイミングとwindow.AutoPagerize.addFilterが設定されるタイミングが違ったりするので、window.AutoPagrizeが設定されたタイミングでさらにwindow.AutoPagerize.addFilterが設定されるのを待つ、みたいなコードが必要になります。

ふつうに書くとひどいコードになるわけですが、これは高階関数にすればすっきりするに違いないと書いてみたらうまく書けたのでうれしかった!のを掘り出してきたけど、今みたら読みたくないです。reduceRightまで入っているのでfx2だと動きません。

function executeOnReadly(parentObject, props, f) {
    var fn = props.reduceRight( function (callback, propname) {
        return function (parentObject) {
            if ( parentObject[propname] ) {
                var newValue = parentObject[propname];
                callback(newValue);
            } else {
                {
                    parentObject.watch( propname, function ( name, oldValue, newValue ) {
                        callback(newValue);
                        unwatch(parentObject, name);
                        return newValue;
                    } );
                }
            }

        };
    }, f);
    fn(parentObject);
}

これを

executeOnReadly(window, ['AutoPagerize', 'addFilter'], function () {console.log("hello")});

と呼び出すと、呼び出したタイミングでwindow.AutoPagerize.addFilterが存在していればそのタイミングでhelloが表示されるし、設定されていなければ設定されたタイミングでhelloが表示される、というはずでしたがGreasemonkey特有の事情でだめでした。

Greasemonkeyじゃないときだと使えるわけですが、watchが必要なのでウェブページで使うわけにもいかないのでFirefox限定だけれどGreasemonkeyはNGなのでFirefox extensionくらいしか使う場面がないので使い道がなくて悲しいです。


About this entry