プロミスでwhileループのようなことをするイディオム的な方法は何でしょう。というわけで。
何かする 条件が成立している場合、もう一度行う 繰り返す その後、別のことをする。
dosomething.then(possilblydomoresomethings).then(finish)
私はこの方法でやってきましたが、もっと良い方法、あるいはもっと簡単な方法はないでしょうか?
var q = require('q');
var index = 1;
var useless = function(){
var currentIndex = index;
console.log(currentIndex)
var deferred = q.defer();
setTimeout(function(){
if(currentIndex > 10)
deferred.resolve(false);
else deferred.resolve(true);
},500);
return deferred.promise;
}
var control = function(cont){
var deferred = q.defer();
if(cont){
index = index + 1;
useless().then(control).then(function(){
deferred.resolve();
});
}
else deferred.resolve();
return deferred.promise;
}
var chain = useless().then(control).then(function(){console.log('done')});
出力してください。 1 2 3 4 5 6 7 8 9 10 11 終了
ここで、再利用可能な機能を紹介すると、かなり分かりやすいと思います。
var Q = require("q");
// `condition` is a function that returns a boolean
// `body` is a function that returns a promise
// returns a promise for the completion of the loop
function promiseWhile(condition, body) {
var done = Q.defer();
function loop() {
// When the result of calling `condition` is no longer true, we are
// done.
if (!condition()) return done.resolve();
// Use `when`, in case `body` does not return a promise.
// When it completes loop again otherwise, if it fails, reject the
// done promise
Q.when(body(), loop, done.reject);
}
// Start running the loop in the next tick so that this function is
// completely async. It would be unexpected if `body` was called
// synchronously the first time.
Q.nextTick(loop);
// The promise
return done.promise;
}
// Usage
var index = 1;
promiseWhile(function () { return index <= 11; }, function () {
console.log(index);
index++;
return Q.delay(500); // arbitrary async
}).then(function () {
console.log("done");
}).done();
私なら、値をラップするためにオブジェクトを使用します。そうすれば、done
プロパティを使って、ループに完了を知らせることができます。
// fn should return an object like
// {
// done: false,
// value: foo
// }
function loop(promise, fn) {
return promise.then(fn).then(function (wrapper) {
return !wrapper.done ? loop(Q(wrapper.value), fn) : wrapper.value;
});
}
loop(Q.resolve(1), function (i) {
console.log(i);
return {
done: i > 10,
value: i++
};
}).done(function () {
console.log('done');
});
このパターンは、現在ではq-flowを使うことでより簡単に呼び出せるようになっています。 例として、上記の問題に対して
var q = require('q');
require('q-flow');
var index = 1;
q.until(function() {
return q.delay(500).then(function() {
console.log(index++);
return index > 10;
});
}).done(function() {
return console.log('done');
});