Node.js
promise
es6
io.js
bluebird

Promiseでwhileループを書いてみる

More than 3 years have passed since last update.

io.jsのES6では標準でPromiseが使えるようになりました。Async.jsで書いてきたコードも、これからはPromiseやGenerator、coなどのコルーチンに移行していきたいです。whileループの書き方がよくわからなかったので、Stack Overflowなどで調べてみました。


リソース

以下のサイトを参考にしました。


Bluebirdの無限ループ

Bluebirdは高機能なPromiseのライブラリです。ES6 Native Promiseにはないメソッドや、コルーチンも用意されています。"Infinite" promise chains, a bad thing? #477にwhileループの例がありました。


infinite-loop-bluebird.es6

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で代用します。


infinite-loop-native.es6

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に書き換えてみました。


while-loop-native

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に再帰を使ったシンプルなループの例がありました。


just-recursion.es6

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ループを書きます。


coroutine-loop.es6

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