node.jsでPromise-Qを利用した非同期処理の繰り返し処理をすんなり理解的できなかったので、ここでまとめます。
※まだnode歴2ヶ月の初心者です
最初のステップ
本家では、こんな感じで紹介されています。(コードを実行可能な状態にmodifyしてあります)
https://github.com/kriskowal/q
without_q.js
var val;
var step1 = function(callback) {
val = 'hoge';
setTimeout(function(){
console.log(val);
callback();
}, 1000);
};
var step2 = function(callback) {
val = val + 'fuga';
setTimeout(function() {
console.log(val);
callback();
}, 500);
};
step1(function(){
step2(function(){
console.log("done");
});
});
これをQを使って書き直します。
with_q.js
var Q = require('q');
var val;
var step1 = function() {
val = 'hoge';
var d = Q.defer();
setTimeout(function(){
console.log(val);
d.resolve();
}, 1000);
return d.promise;
};
var step2 = function() {
val = val + 'fuga';
var d = Q.defer();
setTimeout(function() {
console.log(val);
d.resolve();
}, 500);
return d.promise;
};
Q.fcall(step1)
.then(step2)
.catch(function(error) {
// Handle any error from all above steps
console.log("error");
})
.done(function(){
console.log("done");
});
step1やstep2はpromise objectである必要があります。
ざっくりとしたpromise objectの書き方は、
- 最初に
var d=Q.defer();
を宣言 - 最後に
return d.promise;
を返す - 途中、次の処理と同期をとる処理後に
d.resolve();
を挿入
という感じです。
非同期処理を含む繰り返し処理の同期をとる
StackOverflowで回答を発見しました。
http://stackoverflow.com/questions/17217736/while-loop-with-promises
sample.js
var Q = require('q');
// condition: ループ終了判定
// body: promiseを返す関数
function promiseWhile(condition, body) {
var d = Q.defer();
function loop() {
if (!condition()) return d.resolve();
Q.when(body(), loop, d.reject);
}
Q.nextTick(loop);
return d.promise;
}
// Usage
var index = 1;
promiseWhile(
function() { // condition
return index < 11;
},
function() { // body including arbitrary async
console.log(index);
index++;
return Q.delay(500);
})
.then(function() {
console.log("done"); })
.done();
ここでは、Q.delayを使ったサンプルコードが書かれているのですが、実際には自分のやりたい処理を書く訳で・・・。Q.delayを自分の定義関数に置き換えてみます。
mysample.js
var Q = require('q');
// condition: ループ終了判定
// body: promiseを返す関数
function promiseWhile(condition, body) {
var d = Q.defer();
function loop() {
if (!condition()) return d.resolve();
Q.when(body(), loop, d.reject);
}
Q.nextTick(loop);
return d.promise;
}
// body
var arr = [];
var body = function(idx) {
var d = Q.defer();
setTimeout(function(){
arr.push(idx);
d.resolve();
}, 500);
return d.promise;
};
// Usage
var index = 1;
promiseWhile(
function() { // condition
return index < 11;
},
function() { // body including arbitrary async
console.log(index);
index++;
return body(index-1);
})
.catch(function(error) {
// Handle any error from all above steps
console.log("error");
})
.done(function() {
console.log(arr);
});
つまり、bodyの処理中の非同期処理をpromise objectにすればOKです。