Promise.all同時制限
2877 ワード
背景
通常、コードが複数の非同期処理の後に実行されることを保証する必要があります.
Promise.all(promises: []).then(fun: function);
Promise.all
は、promises
配列内のすべてのpromiseオブジェクトがresolve状態に達してから、then
コールバックを実行することを保証することができる.この場合、
promises
配列の各オブジェクトがhttpリクエストである場合、または各オブジェクトに複雑な呼び出し処理が含まれている場合を考慮します.このような対象は数十万個ある.瞬時に数十万httpリクエストを発行したり(tcp接続数が不足して待機したり)、無数の呼び出しスタックが積み上げられてメモリがオーバーフローしたりする場合があります.
この場合、
Promise.all
の同時制限を考慮する必要があります.Promise.all
同時制限とは、時刻ごとに同時実行されるpromiseの数が固定されているか、最終的な実行結果が元のPromise.all
と一致しているかを指す.インプリメンテーション
promiseは
Promise.all
を呼び出すために実行されるのではなく、promiseオブジェクトをインスタンス化する際に実行されることを知っています.この点を理解した上で、同時制限を実現するにはpromiseインスタンス化から始めるしかありません.すなわち,
promises
配列を生成する制御権を,同時制御ロジックに渡す.ここでは、この機能を一歩一歩実現するつもりはありません.npmには、async-pool、es 6-promise-pool、p-limitなど、この機能を実現するサードパーティ製のパッケージがたくさんあります.ここでは、async-poolのコードを直接持って実現原理を分析します.
コードは簡単で、不要なコードを消して、自分のコメントを加えて、大体の内容は以下の通りです.
function asyncPool(poolLimit, array, iteratorFn) {
let i = 0;
const ret = [];
const executing = [];
const enqueue = function () {
// ,array
if (i === array.length) {
return Promise.resolve();
}
// enqueue, promise
const item = array[i++];
const p = Promise.resolve().then(() => iteratorFn(item, array));
// promises
ret.push(p);
// promise , executing
const e = p.then(() => executing.splice(executing.indexOf(e), 1));
// executing , promise
executing.push(e);
// Promise.rece, executing promise poolLimit, promise
let r = Promise.resolve();
if (executing.length >= poolLimit) {
r = Promise.race(executing);
}
// , array
return r.then(() => enqueue());
};
return enqueue().then(() => Promise.all(ret));
}
promiseに再帰を加えるので、コード注釈には実行順序をあまり表記しませんが、大まかな論理は以下のようにまとめることができます.
array
の第1の要素から始まる、promise
のオブジェクトを初期化するとともに、実行中のpromise executing
配列で保存する.poolLimt
Promise.race
を用いる、executing
におけるpromiseの実行状況を取得し、1つのpromiseの実行が完了すると、promiseの初期化を継続し、executing
におけるPromise.all
は使用方法は次のとおりです.
const timeout = i => new Promise(resolve => setTimeout(() => resolve(i), i));
return asyncPool(2, [1000, 5000, 3000, 2000], timeout).then(results => {
...
});
まとめ
promise同時制限とは,実はpromiseを制御するインスタンス化が根源的である.サードパーティ関数を介している場合はpromiseを作成する制御権をサードパーティに渡せばよい.
しかし、このような実現効果は、本質的に
Promise.all
を捨てて別の道を切り開いた.だからある日promise標準がこの機能を提供することを期待しています