Promiseを使った非同期ループ処理の書き方について
JavaScriptで常に頭を悩ませるのが非同期処理ではないかと思います。非同期処理を幾つも実行したりすると、思ったタイミングで処理が走らないといったことが多々あります。
そんな中でループ処理になると、特に厄介ではないでしょうか。そこで今回はPromiseを使ったループ処理について紹介します。
0から10まで順番に処理をしたら抜けるループ
非同期処理でない場合は次のように書けます。
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
for (var i = 0; i <= 10; i++) { | |
console.log(i) | |
} | |
console.log("Finish"); |
厄介なのは非同期処理時です。まず、基本形として次のようにPromiseを考えます。
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
new Promise(function(res, rej) { | |
}).then(function() { | |
console.log("Finish"); | |
}) |
この処理は一瞬で終了してしまいます。コンソールにも特に何もメッセージは出ません。この中でループ処理を行うようにします。この時、注意するのは非同期処理がターゲットと言うことです。そのため、loop関数の中でもPromiseを使って処理を行うようにして、処理順番を保証します。
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
new Promise(function(res, rej) { | |
function loop(i) { | |
return new Promise(function(resolve, reject) { | |
setTimeout(function() { | |
console.log(i); | |
resolve(i+1); | |
}, 100); | |
}) | |
} | |
loop(0); | |
}).then(function() { | |
console.log("Finish"); | |
}) |
これにより、非同期処理(setTimeout)が実行された後、resolveが実行されるようになります。後は内側のPromiseについて、then処理を書きます。
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// ループ処理の完了を受け取るPromise | |
new Promise(function(res, rej) { | |
// ループ処理(再帰的に呼び出し) | |
function loop(i) { | |
// 非同期処理なのでPromiseを利用 | |
return new Promise(function(resolve, reject) { | |
// 非同期処理部分 | |
setTimeout(function() { | |
console.log(i); | |
// resolveを呼び出し | |
resolve(i+1); | |
}, 100); | |
}) | |
.then(function(count) { | |
// ループを抜けるかどうかの判定 | |
if (count > 10) { | |
// 抜ける(外側のPromiseのresolve判定を実行) | |
res(); | |
} else { | |
// 再帰的に実行 | |
loop(count); | |
} | |
}); | |
} | |
// 初回実行 | |
loop(0); | |
}).then(function() { | |
// ループ処理が終わったらここにくる | |
console.log("Finish"); | |
}) |
thenの中で処理判定を行い、ループを抜けるかどうかの判定を行っています。これで非同期処理における処理順番の保証と、処理完了時に次の処理につながる部分ができました。
JavaScriptにおける非同期処理のループはかなり面倒であるというのが分かるかと思います。JSDoc: Namespace: asyncを使うと非同期処理におけるループがこれくらい分かりやすく書けるようになります。以下は処理を5回繰り返すという指定です。
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
var loop = h5.async.loop(new Array(5), function (i, v) { | |
var deffered = h5.async.deferred(); | |
var loop = function (count) { | |
setTimeout(function () { | |
console.log(count); | |
deffered.resolve(count); | |
}, 100); | |
}; | |
loop(i); | |
return deffered.promise(); | |
}).done(function () { | |
console.log("処理完了"); | |
}); |
コメントは受け付けていません。