io.jsのES6では標準でPromiseが使えるようになりました。Async.jsで書いてきたコードも、これからはPromiseやGenerator、coなどのコルーチンに移行していきたいです。whileループの書き方がよくわからなかったので、Stack Overflowなどで調べてみました。
リソース
以下のサイトを参考にしました。
- "Infinite" promise chains, a bad thing? #477
- Bluebird
- While loop with promises
- While loop using bluebird promises
- Correct way to write loops for promise.
- Memory leak trying to create a while loop with promises #502
Bluebirdの無限ループ
Bluebirdは高機能なPromiseのライブラリです。ES6 Native Promiseにはないメソッドや、コルーチンも用意されています。"Infinite" promise chains, a bad thing? #477にwhileループの例がありました。
var Promise = require('bluebird');
Promise.resolve(0).then(function loop(i) {
return new Promise(function(resolve, reject) {
console.log(i);
resolve(i+1);
})
.delay(100)
.then(loop);
});
プログラムはio.jsの3.2.0、Bluebirdは2.9.34実行しました。
$ node -v
v3.2.0
実行結果
1秒間隔で数字がインクリメントされます。無限ループなので適当なところで止めます。
$ node infinite-loop-bluebird.es6
0
1
2
3
...
Promise.delay()
を先に書くこともできます。
var Promise = require('bluebird');
Promise.resolve(0).then(function loop(i) {
return Promise.delay(100).then(function() {
console.log(i);
return i+1;
})
.then(loop);
});
ES6 Native Promiseの無限ループ
上記をES6 Native Promiseに書き換えてみます。ES6ネイティブにはBluebirdのdelayや、QのdelayがないのでsetTimeout
で代用します。
Promise.resolve(0).then(function loop(i) {
return new Promise(function(resolve, reject) {
setTimeout(function() {
console.log(i);
resolve(i+1);
}, 100);
})
.then(loop);
});
実行結果
Bluebirdの結果と同じです。
$ node infinite-loop-native.es6
0
1
2
3
...
ES6 Native Promiseのwhile ループ
While loop with promisesにあるのは、Qの例ですがES6 Native Promiseに書き換えてみました。
function action(i) {
console.log(i);
return {
done: i > 9,
value: i+1
};
}
function loop(promise, fn) {
return promise.then(fn).then(function(wrapper) {
return !wrapper.done ? loop(Promise.resolve(wrapper.value), fn): wrapper.value;
});
}
loop(Promise.resolve(0), action).then(function(value) {console.log('end: ' + value)});
実行結果
数字がインクリメントされ、i > 9
がtrueになるとループが停止します。
$ node while-loop-native.es6
0
1
2
3
4
5
6
7
8
9
10
end: 11
Bluebirdのwhile ループ
Memory leak trying to create a while loop with promises #502に再帰を使ったシンプルなループの例がありました。
var Promise = require('bluebird');
(function loop(i) {
if (i < 10) {
return Promise.delay(100).then(function() {
console.log(i);
return i+1;
}).then(loop);
}
return Promise.resolve(i);
})(0);
実行結果
$ node just-recursion.es6
0
1
2
3
4
5
6
7
8
9
10
もうひとつはBluebirdのPromise.coroutineを使う例です。ジェネレーターの中でwhileループを書きます。
var Promise = require('bluebird');
function condition(i) {
return i > 10;
}
function action(i) {
console.log(i);
return Promise.resolve(i+1);
}
var loop = Promise.coroutine(function* (condition, action, value) {
while(!condition(value)) {
value = yield action(value);
yield Promise.delay(100);
}
});
loop(condition, action, 0);
実行結果
$ node coroutine-loop.es6
0
1
2
3
4
5
6
7
8
9
10