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そのものを引き渡しても良い。