JavaScript: defer関数でいろいろ

JavaScript: 普通に同期処理 - mayokara note

var defer = function(/* f1, f2, ... */){
    var self = this, fn = Array.prototype.slice.call(arguments);
    (function(/* arg2, arg3, ... */){
        var args = Array.prototype.slice.call(arguments);
        (fn.shift()).apply(self, [arguments.callee].concat(args));
    })();
};

この関数の説明をきちんと書いてなかったので補足。

bind

deferのthisが、引数に与えた関数すべてのthisになる。

defer.call(this, F1, F2, F3);

のように呼ぶことで、F1、F2、F3すべてにbind(this)(Prototype.js)するのと同じ効果が得られる。

wait(あるいはsleep、pause)

1秒のwaitを置く例。

defer(function(next){
    alert("1000ms wait");
    window.setTimeout(function(){ next("finished"); }, 1000);
}, function(next, str){
    alert(str); // -> (alert "finished")
});

deferもnextも、いくつでも引数を取れる。

nextに与えた第n引数は次の関数の第n+1引数になる。

第1引数はnext(次の関数を呼ぶ関数)で固定。

各関数の返り値は意味を持たない。defer自身も値を返さない。

setTimeoutの代わりに(GM_)XMLHttpRequestを入れれば、onloadが呼ばれるまで次の関数の実行を遅延できる。

loop

defer(function loop(next, i){
    i = i || 0;
    if (!(i < 5)) return next();
    console.log("count " + i);
    window.setTimeout(function(){ loop(next, i+1); }, 1000);
}, function(next){
    console.log("5 times loop finished");
});
count 0
count 1
count 2
count 3
count 4
5 times loop finished

nextをきちんと引き継げばloopもできる。

setTimeoutに与えた関数の中ではarguments.calleeが使えないので、ループさせる関数にloopという名前をつけ、それを使う。

前述のdefer.callでthisをbindしている場合は、それに合わせてloopにthisをセットする必要がある。

defer(function(next){
    next(0);
}, function loop(next, i){
    if (!(i < 5)) return next();
    console.log("count " + i);
    window.setTimeout(function(){ loop(next, i+1); }, 1000);
}, function(next){
    console.log("5 times loop finished");
});

毎回i = i || 0;を実行するのが無駄に感じる場合はこうする。

同様にして、特定の条件を満たすまで次の関数の実行を引き伸ばしたりできる。

parallel

defer(function(next){
    var obj = {},
        stock = function(key, value){
            obj[key] = value;
            // 全部揃ったらnextを呼ぶ
            if (obj.a && obj.b && obj.c) next(obj.a, obj.b, obj.c);
        };
    window.setTimeout(function(){ stock("a", "AAAAA"); }, 3000);
    window.setTimeout(function(){ stock("b", "BBBBB"); }, 2000);
    window.setTimeout(function(){ stock("c", "CCCCC"); }, 5000);
}, function(next, a, b, c){
    alert(a + b + c); // (alert "AAAAABBBBBCCCCC")
});

setTimeoutの代わりに、(GM_)XMLHttpRequestなどが入る。

objそのものを引き渡しても良い。

Comment: 0

Comment Form
Name
URL
Comment

Trackback: 0

Trackback URL
http://mayokara.info/note/trackback/325
Attention
スパム対策のため、当エントリへのリンクがないトラックバックをブロックしています。