ホームページ > 記事 > ウェブフロントエンド > 同時実行制御とは何ですか? JavaScript で同時実行制御を実装するにはどうすればよいですか?
日々の開発プロセスでは、同時リクエストの数の制御など、同時実行制御シナリオに遭遇することがあります。では、JavaScript で同時実行制御を実装するにはどうすればよいでしょうか?この質問に答える前に、同時実行制御について簡単に説明しましょう。
実行する To Do タスクが 6 つあり、同時に実行できるタスクの数を制限したいとします。つまり、同時に実行できるタスクは最大 2 つです。 。 実行タスク リストのいずれかのタスクが完了すると、プログラムは自動的にToDoタスク リストから新しいToDoタスクを取得し、そのタスクを実行タスク リストに追加します。 。上記のプロセスを誰もがより直感的に理解できるように、Abago は特別に次の 3 つの絵を描きました:
# #わかりました、同時実行制御の導入後、アバオ兄弟は Github の async-pool ライブラリを使用して、非同期タスク同時実行制御の具体的な実装を紹介します。
async-pool:https://github.com/rxaviers/async-pool
ネイティブ ES6/ を使用して、制限された同時実行で複数の Promise を返す関数と非同期関数を実行します。 ES7。
async-pool このライブラリは、ES7 と ES6 の 2 つの異なるバージョンの実装を提供します。具体的な実装については、その使用方法を見てみましょう。
const timeout = i => new Promise(resolve => setTimeout(() => resolve(i), i)); await asyncPool(2, [1000, 5000, 3000, 2000], timeout);
上記のコードでは、このライブラリ asyncPool# によって提供される async-pool を使用します。 ## 非同期タスクの同時実行制御を実装する機能。
asyncPool 関数のシグネチャは次のとおりです:
function asyncPool(poolLimit, array, iteratorFn){ ... }関数は 3 つのパラメータを受け取ります:
関数を使用した後の、対応する実行プロセスは次のとおりです。 <pre class="brush:js;toolbar:false;">const timeout = i => new Promise(resolve => setTimeout(() => resolve(i), i));
await asyncPool(2, [1000, 5000, 3000, 2000], timeout);
// Call iterator (i = 1000)
// Call iterator (i = 5000)
// Pool limit of 2 reached, wait for the quicker one to complete...
// 1000 finishes
// Call iterator (i = 3000)
// Pool limit of 2 reached, wait for the quicker one to complete...
// 3000 finishes
// Call iterator (i = 2000)
// Itaration is complete, wait until running ones complete...
// 5000 finishes
// 2000 finishes
// Resolves, results are passed in given array order `[1000, 5000, 3000, 2000]`.</pre>
上記のアノテーション情報を観察することで、次のことがわかります。
関数内の制御フローを大まかに理解します。まず、asyncPool
関数の ES7 実装を分析しましょう。
2.2 asyncPool ES7 の実装
async function asyncPool(poolLimit, array, iteratorFn) { const ret = []; // 存储所有的异步任务 const executing = []; // 存储正在执行的异步任务 for (const item of array) { // 调用iteratorFn函数创建异步任务 const p = Promise.resolve().then(() => iteratorFn(item, array)); ret.push(p); // 保存新的异步任务 // 当poolLimit值小于或等于总任务个数时,进行并发控制 if (poolLimit <= array.length) { // 当任务完成后,从正在执行的任务数组中移除已完成的任务 const e = p.then(() => executing.splice(executing.indexOf(e), 1)); executing.push(e); // 保存正在执行的异步任务 if (executing.length >= poolLimit) { await Promise.race(executing); // 等待较快的任务执行完成 } } } return Promise.all(ret); }
と Promise.race
の機能が最大限に活用されています。 . ES7 で提供されている async await
機能と組み合わせることで、ついに同時実行制御機能が実現します。ステートメントの await Promise.race(executing);
行を使用すると、実行タスク リスト
内のより高速なタスクが完了するのを待ってから、次のサイクルの実行を続行します。 asyncPool ES7 の実装は比較的シンプルです。次に、
機能を使用せずに同じ機能を実現する方法を見てみましょう。 2.3 asyncPool ES6 実装
function asyncPool(poolLimit, array, iteratorFn) { let i = 0; const ret = []; // 存储所有的异步任务 const executing = []; // 存储正在执行的异步任务 const enqueue = function () { if (i === array.length) { return Promise.resolve(); } const item = array[i++]; // 获取新的任务项 const p = Promise.resolve().then(() => iteratorFn(item, array)); ret.push(p); let r = Promise.resolve(); // 当poolLimit值小于或等于总任务个数时,进行并发控制 if (poolLimit <= array.length) { // 当任务完成后,从正在执行的任务数组中移除已完成的任务 const e = p.then(() => executing.splice(executing.indexOf(e), 1)); executing.push(e); if (executing.length >= poolLimit) { r = Promise.race(executing); } } // 正在执行任务列表 中较快的任务执行完成之后,才会从array数组中获取新的待办任务 return r.then(() => enqueue()); }; return enqueue().then(() => Promise.all(ret)); }
関数を通じて実装されます。 Promise.race(executing)
によって返された Promise
オブジェクトが完了すると、array
Get new から enqueue
関数が呼び出されます。配列からの ToDo タスク。 3. A Bao 兄弟から言いたいことがあります
の ES7 および ES6 の特定の実装では、Promise.all
および Promise.race
関数。その中でも、手書きの Promise.all
は面接でよく聞かれる質問です。この機会を利用して、A Bao 兄弟と皆さんは、Promise.all
関数と Promise.race
関数の簡単なバージョンを手書きするようになりました。 <h4 data-id="heading-9">3.1 手写 Promise.all</h4>
<p><strong><code>Promise.all(iterable)
方法会返回一个 promise 对象,当输入的所有 promise 对象的状态都变成 resolved
时,返回的 promise 对象就会以数组的形式,返回每个 promise 对象 resolve 后的结果。当输入的任何一个 promise 对象状态变成 rejected
时,则返回的 promise 对象会 reject 对应的错误信息。
Promise.all = function (iterators) { return new Promise((resolve, reject) => { if (!iterators || iterators.length === 0) { resolve([]); } else { let count = 0; // 计数器,用于判断所有任务是否执行完成 let result = []; // 结果数组 for (let i = 0; i < iterators.length; i++) { // 考虑到iterators[i]可能是普通对象,则统一包装为Promise对象 Promise.resolve(iterators[i]).then( (data) => { result[i] = data; // 按顺序保存对应的结果 // 当所有任务都执行完成后,再统一返回结果 if (++count === iterators.length) { resolve(result); } }, (err) => { reject(err); // 任何一个Promise对象执行失败,则调用reject()方法 return; } ); } } }); };
需要注意的是对于 Promise.all
的标准实现来说,它的参数是一个可迭代对象,比如 Array、String 或 Set 等。
Promise.race(iterable)
方法会返回一个 promise 对象,一旦迭代器中的某个 promise 对象 resolved 或 rejected,返回的 promise 对象就会 resolve 或 reject 相应的值。
Promise.race = function (iterators) { return new Promise((resolve, reject) => { for (const iter of iterators) { Promise.resolve(iter) .then((res) => { resolve(res); }) .catch((e) => { reject(e); }); } }); };
本文阿宝哥带大家详细分析了 async-pool 异步任务并发控制的具体实现,同时为了让大家能够更好地理解 async-pool 的核心代码。最后阿宝哥还带大家一起手写简易版的 Promise.all
和 Promise.race
函数。其实除了 Promise.all
函数之外,还存在另一个函数 —— Promise.allSettled
,该函数用于解决 Promise.all
存在的问题,感兴趣的小伙伴可以自行研究一下。
更多编程相关知识,请访问:编程视频!!
以上が同時実行制御とは何ですか? JavaScript で同時実行制御を実装するにはどうすればよいですか?の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。