ホームページ  >  記事  >  ウェブフロントエンド  >  同時実行制御とは何ですか? JavaScript で同時実行制御を実装するにはどうすればよいですか?

同時実行制御とは何ですか? JavaScript で同時実行制御を実装するにはどうすればよいですか?

青灯夜游
青灯夜游転載
2021-06-28 10:50:553037ブラウズ

同時実行制御とは何ですか? JavaScript で同時実行制御を実装するにはどうすればよいですか?

日々の開発プロセスでは、同時リクエストの数の制御など、同時実行制御シナリオに遭遇することがあります。では、JavaScript で同時実行制御を実装するにはどうすればよいでしょうか?この質問に答える前に、同時実行制御について簡単に説明しましょう。

実行する To Do タスクが 6 つあり、同時に実行できるタスクの数を制限したいとします。つまり、同時に実行できるタスクは最大 2 つです。 。 実行タスク リストのいずれかのタスクが完了すると、プログラムは自動的にToDoタスク リストから新しいToDoタスクを取得し、そのタスクを実行タスク リストに追加します。 。上記のプロセスを誰もがより直感的に理解できるように、Abago は特別に次の 3 つの絵を描きました:

1.1 Stage 1

同時実行制御とは何ですか? JavaScript で同時実行制御を実装するにはどうすればよいですか?

1.2 フェーズ 2

同時実行制御とは何ですか? JavaScript で同時実行制御を実装するにはどうすればよいですか?

1.3 フェーズ 3

同時実行制御とは何ですか? JavaScript で同時実行制御を実装するにはどうすればよいですか?

# #わかりました、同時実行制御の導入後、アバオ兄弟は Github の async-pool ライブラリを使用して、非同期タスク同時実行制御の具体的な実装を紹介します。

async-pool:https://github.com/rxaviers/async-pool

ネイティブ ES6/ を使用して、制限された同時実行で複数の Promise を返す関数と非同期関数を実行します。 ES7。

2. 同時実行制御の実装

async-pool このライブラリは、ES7 と ES6 の 2 つの異なるバージョンの実装を提供します。具体的な実装については、その使用方法を見てみましょう。

2.1 asyncPool の使用

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 つのパラメータを受け取ります:

    ##poolLimit
  • (数値型): を表します。制限された同時実行数;
  • array
  • (配列タイプ): タスク配列を表します;
  • iteratorFn
  • (関数タイプ): 反復を表します関数、使用 各タスク項目の処理を実装するために、この関数は Promise オブジェクトまたは非同期関数を返します。
  • 上記の例では、
asyncPool

関数を使用した後の、対応する実行プロセスは次のとおりです。 <pre class="brush:js;toolbar:false;">const timeout = i =&gt; new Promise(resolve =&gt; setTimeout(() =&gt; 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

関数内の制御フローを大まかに理解します。まず、asyncPool 関数の ES7 実装を分析しましょう。

「不死へのフルスタックの道」に従って、ブラザー・ア・バオがオリジナルで作成した 4 冊の無料電子書籍 (合計 30,000 ダウンロード) と 50 以上の TS シリーズ チュートリアルを読んでください。

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.all

Promise.race の機能が最大限に活用されています。 . ES7 で提供されている async await 機能と組み合わせることで、ついに同時実行制御機能が実現します。ステートメントの await Promise.race(executing); 行を使用すると、実行タスク リスト 内のより高速なタスクが完了するのを待ってから、次のサイクルの実行を続行します。 asyncPool ES7 の実装は比較的シンプルです。次に、

async await

機能を使用せずに同じ機能を実現する方法を見てみましょう。 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));
}

ES6 実装バージョンでは、コア制御ロジックは、内部的にカプセル化された

enqueue

関数を通じて実装されます。 Promise.race(executing) によって返された Promise オブジェクトが完了すると、array Get new から enqueue 関数が呼び出されます。配列からの ToDo タスク。 3. A Bao 兄弟から言いたいことがあります

asyncPool

の 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 等。

3.2 手写 Promise.race

Promise.race(iterable) 方法会返回一个 promise 对象,一旦迭代器中的某个 promise 对象 resolvedrejected,返回的 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.allPromise.race 函数。其实除了 Promise.all 函数之外,还存在另一个函数 —— Promise.allSettled,该函数用于解决 Promise.all 存在的问题,感兴趣的小伙伴可以自行研究一下。

四、参考资源

更多编程相关知识,请访问:编程视频!!

以上が同時実行制御とは何ですか? JavaScript で同時実行制御を実装するにはどうすればよいですか?の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事はjuejin.cnで複製されています。侵害がある場合は、admin@php.cn までご連絡ください。