すべてのfor
非同期操作が開始されている間、ループはすぐに完了するまで実行されます。将来、それらが完了してコールバックを呼び出すと、ループ インデックス変数i
の値はすべてのコールバックの最後の値になります。
これは、for
ループが非同期操作の完了を待たずにループの次の繰り返しに進むためであり、非同期コールバックは後で呼び出されるためです。したがって、ループは反復を完了し、それらの非同期操作が終了すると、コールバックが呼び出されます。そのため、ループ インデックスは「完了」し、すべてのコールバックの最終値にとどまります。
これを回避するには、コールバックごとに個別にループ インデックスを一意に保存する必要があります。Javascript でこれを行う方法は、それを関数クロージャでキャプチャすることです。これは、この目的専用のインライン関数クロージャーを作成することで実行できます (最初の例を以下に示します)。または、インデックスを渡す外部関数を作成して、インデックスを一意に維持できるようにすることもできます (2 番目の例を以下に示します)。
2016 年の時点で、完全に仕様に準拠した JavaScript の ES6 実装を使用している場合は、ループ変数let
を定義するために使用することもでき、for
ループの反復ごとに一意に定義されfor
ます (以下の 3 番目の実装)。ただし、これは ES6 実装の後期実装機能であるため、実行環境がそのオプションをサポートしていることを確認する必要があります。
独自の関数クロージャーを作成するため、.forEach() を使用して反復します
someArray.forEach(function(item, i) {
asynchronousProcess(function(item) {
console.log(i);
});
});
IIFE を使用して独自の関数クロージャを作成する
var j = 10;
for (var i = 0; i < j; i++) {
(function(cntr) {
// here the value of i was passed into as the argument cntr
// and will be captured in this function closure so each
// iteration of the loop can have it's own value
asynchronousProcess(function() {
console.log(cntr);
});
})(i);
}
外部関数を作成または変更して変数に渡す
関数を変更できる場合はasynchronousProcess()
、そこに値を渡すだけで、次のようにasynchronousProcess()
関数を cntr コールバックに戻すことができます。
var j = 10;
for (var i = 0; i < j; i++) {
asynchronousProcess(i, function(cntr) {
console.log(cntr);
});
}
ES6を使用let
ES6 を完全にサポートする Javascript 実行環境がある場合は、次のようにループで使用できlet
ます。for
const j = 10;
for (let i = 0; i < j; i++) {
asynchronousProcess(function() {
console.log(i);
});
}
let
このようにループ宣言で宣言すると、ループの呼び出しごとfor
に for の一意の値が作成さi
れます (これが必要です)。
promise と async/await を使用したシリアル化
非同期関数が promise を返し、非同期操作を並列ではなく次々に実行するようにシリアル化し、 and をサポートする最新の環境で実行しているasync
場合await
は、さらに多くのオプションがあります。
async function someFunction() {
const j = 10;
for (let i = 0; i < j; i++) {
// wait for the promise to resolve before advancing the for loop
await asynchronousProcess();
console.log(i);
}
}
これにより、 への呼び出しが一度に 1 つだけ実行asynchronousProcess()
され、for
それぞれが完了するまでループが進行しないことが保証されます。これは、非同期操作をすべて並行して実行する以前のスキームとは異なるため、必要な設計に完全に依存します。注: await
promise で動作するため、関数は、非同期操作が完了したときに解決/拒否される promise を返す必要があります。また、 を使用するawait
には、含まれている関数を宣言する必要があることに注意してくださいasync
。
非同期操作を並行して実行Promise.all()
し、結果を順番に収集するために使用します
function someFunction() {
let promises = [];
for (let i = 0; i < 10; i++) {
promises.push(asynchonousProcessThatReturnsPromise());
}
return Promise.all(promises);
}
someFunction().then(results => {
// array of results in order here
console.log(results);
}).catch(err => {
console.log(err);
});