Promise の all()、race()、および allSettled() メソッドを理解する

青灯夜游
リリース: 2020-12-14 17:51:48
転載
3725 人が閲覧しました

Promise の all()、race()、および allSettled() メソッドを理解する

ES6 以降では、主に Promise.all()Promise.race()Promise.allSettled( ) この提案はステージ 4 に達したため、ECMAScript 2020 の一部となる予定です。

1. 概要

##Promise.all(promise: Iterable>): Promise> ;

    Promise.all(iterable)
  • メソッドは Promise インスタンスを返します。このインスタンスは iterable パラメーター内のすべての promise が「解決」されるか、パラメーターに promise が含まれていない場合、コールバックは完了 (解決) します。 ## 失敗があります (拒否)、このインスタンスのコールバックは失敗します (拒否)、失敗の理由は最初に失敗した結果です promisePromise .race
  • (promises: Iterable>): Promise

    #Promise.race(iterable) ) このメソッドは、反復子の

    promise
      が解決または拒否されると、解決または拒否される
    • promise を返します。 Promise.allSettled(promises: Iterable>): Promise>>

    Promise.allSettled() メソッドは、promise ##promise

    が持つ
      promise
    • を返します。解決されたか拒否された後に解決され、各オブジェクトは各 promise の結果を記述します。 2. 復習: Promise の状態Promise
    • を返す非同期操作がある場合、これらは
    Promise# です。 ## 考えられる状態:

    pending: 初期状態、成功でも失敗でもありません。

    fulfilled: 操作が正常に完了したことを意味します。 拒否: 操作が失敗したことを意味します。 解決済み:

    約束
      完了または拒否されました。
    • 約束
    • 一度達成するとステータスは変わりません。
    • 3. 組み合わせとは
    • 部分全体モードとも呼ばれ、オブジェクトをツリー構造に統合します。 to 「部分-全体」構造の階層を表します。合成モードでは、単一オブジェクトと複合オブジェクトを一貫して使用できるようになり、次の 2 つの関数に基づいています:

    Promise の all()、race()、および allSettled() メソッドを理解するプリミティブ関数 (略してプリミティブ) はアトミック ブロックを作成します。

    コンポジション関数 (コンポジションと略記) は、原子や複合体を組み合わせて複合体を形成します。

    JS Promises の場合

      プリミティブ関数には次のものが含まれます:
    • Promise.resolve()
    • ,
    • Promise.reject()

    結合関数:

    Promise.all()
    • Promise.race()Promise.allSettled()
    • 4. Promise.all()Promise.all():

    #Promise の型シグネチャ。 all(promises: Iterable>): Promise>
    • 戻り状況: フルフィルメント: 受信した反復可能オブジェクトが空の場合、Promise.all
    • は完了 (
    resolved

    ) ステータス

    を同期的に返します。約束###。

    すべての受信 promise が完了するか、受信反復可能オブジェクトに
    promise がない場合、Promise.allpromise を返します。非同期的に満たされます。 いずれの場合も、
    Promise.all によって返される promise の完了ステータスの結果は配列であり、すべての受信反復パラメーター オブジェクトの値が含まれます (約束されていない値も含みます)。 失敗/拒否: 受信した
    promise の 1 つが失敗した場合 (rejected)、 Promise.all

    他の

    promise が完了したかどうかに関係なく、失敗した結果を失敗ステータスのコールバック関数に非同期的に渡します。
    例を見てみましょう: <div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:js;toolbar:false;"> const promises = [ Promise.resolve(&amp;#39;a&amp;#39;), Promise.resolve(&amp;#39;b&amp;#39;), Promise.resolve(&amp;#39;c&amp;#39;), ]; Promise.all(promises) .then((arr) =&gt; assert.deepEqual( arr, [&amp;#39;a&amp;#39;, &amp;#39;b&amp;#39;, &amp;#39;c&amp;#39;] ));</pre><div class="contentsignin">ログイン後にコピー</div></div>Promise の 1 つが拒否された場合に何が起こるか: <div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:js;toolbar:false;"> const promises = [ Promise.resolve(&amp;#39;a&amp;#39;), Promise.resolve(&amp;#39;b&amp;#39;), Promise.reject(&amp;#39;ERROR&amp;#39;), ]; Promise.all(promises) .catch((err) =&gt; assert.equal( err, &amp;#39;ERROR&amp;#39; ));</pre><div class="contentsignin">ログイン後にコピー</div></div>次の図は、Promise.all() を示しています。仕組み

    4.1 Asynchronous.map() および Promise.all()

    配列変換メソッド (

    .map( )# など) ##、.filter() など、同期計算に使用されます。たとえば、

        function timesTwoSync(x) {
          return 2 * x;
        }
        const arr = [1, 2, 3];
        const result = arr.map(timesTwoSync);
        assert.deepEqual(result, [2, 4, 6]);
    ログイン後にコピー

    Promise の all()、race()、および allSettled() メソッドを理解する.map()

    のコールバックが

    Promise

    に基づく関数の場合はどうなりますか?このメソッドを使用すると、

    .map() によって返される結果は Promises の配列になります。

    Promises数组不是普通代码可以使用的数据,但我们可以通过Promise.all()来解决这个问题:它将Promises数组转换为Promise,并使用一组普通值数组来实现。

        function timesTwoAsync(x) {
          return new Promise(resolve => resolve(x * 2));
        }
        const arr = [1, 2, 3];
        const promiseArr = arr.map(timesTwoAsync);
        Promise.all(promiseArr)
          .then(result => {
            assert.deepEqual(result, [2, 4, 6]);
          });
    ログイン後にコピー

    更实际工作上关于 .map()示例

    接下来,咱们使用.map()Promise.all()Web下载文件。 首先,咱们需要以下帮助函数:

        function downloadText(url) {
          return fetch(url)
            .then((response) => { // (A)
              if (!response.ok) { // (B)
                throw new Error(response.statusText);
              }
              return response.text(); // (C)
            });
        }
    ログイン後にコピー

    downloadText()使用基于Promise的fetch API 以字符串流的方式下载文件:

    • 首先,它异步检索响应(第A行)。
    • response.ok(B行)检查是否存在“找不到文件”等错误。
    • 如果没有错误,使用.text()(第C行)以字符串的形式取回文件的内容。

    在下面的示例中,咱们 下载了两个文件

        const urls = [
          &#39;http://example.com/first.txt&#39;,
          &#39;http://example.com/second.txt&#39;,
        ];
    
        const promises = urls.map(
          url => downloadText(url));
        
        Promise.all(promises)
          .then(
            (arr) => assert.deepEqual(
              arr, [&#39;First!&#39;, &#39;Second!&#39;]
            ));
    ログイン後にコピー

    Promise.all()的一个简版实现

        function all(iterable) {
          return new Promise((resolve, reject) => {
            let index = 0;
            for (const promise of iterable) {
              // Capture the current value of `index`
              const currentIndex = index;
              promise.then(
                (value) => {
                  if (anErrorOccurred) return;
                  result[currentIndex] = value;
                  elementCount++;
                  if (elementCount === result.length) {
                    resolve(result);
                  }
                },
                (err) => {
                  if (anErrorOccurred) return;
                  anErrorOccurred = true;
                  reject(err);
                });
              index++;
            }
            if (index === 0) {
              resolve([]);
              return;
            }
            let elementCount = 0;
            let anErrorOccurred = false;
            const result = new Array(index);
          });
        }
    ログイン後にコピー

    5. Promise.race()

    Promise.race()方法的定义:

    Promise.race(promises: Iterable>): Promise

    Promise.race(iterable) 方法返回一个 promise,一旦迭代器中的某个promise解决或拒绝,返回的 promise就会解决或拒绝。来几个例子,瞧瞧:

    const promises = [
      new Promise((resolve, reject) =>
        setTimeout(() => resolve(&#39;result&#39;), 100)), // (A)
      new Promise((resolve, reject) =>
        setTimeout(() => reject(&#39;ERROR&#39;), 200)), // (B)
    ];
    Promise.race(promises)
      .then((result) => assert.equal( // (C)
        result, &#39;result&#39;));
    ログイン後にコピー

    在第 A 行,Promise 是完成状态 ,所以 第 C 行会执行(尽管第 B 行被拒绝)。

    如果 Promise 被拒绝首先执行,在来看看情况是嘛样的:

        const promises = [
          new Promise((resolve, reject) =>
            setTimeout(() => resolve(&#39;result&#39;), 200)),
          new Promise((resolve, reject) =>
            setTimeout(() => reject(&#39;ERROR&#39;), 100)),
        ];
        Promise.race(promises)
          .then(
            (result) => assert.fail(),
            (err) => assert.equal(
              err, &#39;ERROR&#39;));
    ログイン後にコピー

    注意,由于 Promse 先被拒绝,所以 Promise.race() 返回的是一个被拒绝的 Promise

    这意味着Promise.race([])的结果永远不会完成。

    下图演示了Promise.race()的工作原理:

    Promise の all()、race()、および allSettled() メソッドを理解する

    Promise.race() 在 Promise 超时下的情况

    在本节中,我们将使用Promise.race()来处理超时的 Promise。 以下辅助函数:

        function resolveAfter(ms, value=undefined) {
          return new Promise((resolve, reject) => {
            setTimeout(() => resolve(value), ms);
          });
        }
    ログイン後にコピー

    resolveAfter() 主要做的是在指定的时间内,返回一个状态为 resolvePromise,值为为传入的 value

    调用上面方法:

        function timeout(timeoutInMs, promise) {
          return Promise.race([
            promise,
            resolveAfter(timeoutInMs,
              Promise.reject(new Error(&#39;Operation timed out&#39;))),
          ]);
        }
    ログイン後にコピー

    timeout() 返回一个Promise,该 Promise 的状态取决于传入 promise 状态 。

    其中 timeout 函数中的 resolveAfter(timeoutInMs, Promise.reject(new Error(&#39;Operation timed out&#39;)) ,通过 resolveAfter 定义可知,该结果返回的是一个被拒绝状态的 Promise

    再来看看timeout(timeoutInMs, promise)的运行情况。如果传入promise在指定的时间之前状态为完成时,timeout 返回结果就是一个完成状态的 Promise,可以通过.then的第一个回调参数处理返回的结果。

        timeout(200, resolveAfter(100, &#39;Result!&#39;))
          .then(result => assert.equal(result, &#39;Result!&#39;));
    ログイン後にコピー

    相反,如果是在指定的时间之后完成,刚 timeout 返回结果就是一个拒绝状态的 Promise,从而触发catch方法指定的回调函数。

        timeout(100, resolveAfter(2000, &#39;Result!&#39;))
          .catch(err => assert.deepEqual(err, new Error(&#39;Operation timed out&#39;)));
    ログイン後にコピー

    重要的是要了解“Promise 超时”的真正含义:

    1. 如果传入入Promise 较到的得到解决,其结果就会给返回的 Promise
    2. 如果没有足够快得到解决,输出的 Promise 的状态为拒绝。

    也就是说,超时只会阻止传入的Promise,影响输出 Promise(因为Promise只能解决一次), 但它并没有阻止传入Promise的异步操作。

    5.2 Promise.race() 的一个简版实现

    以下是 Promise.race()的一个简化实现(它不执行安全检查)

        function race(iterable) {
          return new Promise((resolve, reject) => {
            for (const promise of iterable) {
              promise.then(
                (value) => {
                  if (settlementOccurred) return;
                  settlementOccurred = true;
                  resolve(value);
                },
                (err) => {
                  if (settlementOccurred) return;
                  settlementOccurred = true;
                  reject(err);
                });
            }
            let settlementOccurred = false;
          });
        }
    ログイン後にコピー

    6.Promise.allSettled()

    “Promise.allSettled”这一特性是由Jason WilliamsRobert PamelyMathias Bynens提出。

    promise.allsettle()方法的定义:

    • Promise.allSettled(promises: Iterable<Promise>)
      : Promise<Array>>

    它返回一个ArrayPromise,其元素具有以下类型特征:

        type SettlementObject<T> = FulfillmentObject<T> | RejectionObject;
        
        interface FulfillmentObject<T> {
          status: &#39;fulfilled&#39;;
          value: T;
        }
        
        interface RejectionObject {
          status: &#39;rejected&#39;;
          reason: unknown;
        }
    ログイン後にコピー

    Promise.allSettled()方法返回一个promise,该promise在所有给定的promise已被解析或被拒绝后解析,并且每个对象都描述每个promise的结果。

    举例说明, 比如各位用户在页面上面同时填了3个独立的表单, 这三个表单分三个接口提交到后端, 三个接口独立, 没有顺序依赖, 这个时候我们需要等到请求全部完成后给与用户提示表单提交的情况

    在多个promise同时进行时咱们很快会想到使用Promise.all来进行包装, 但是由于Promise.all的短路特性, 三个提交中若前面任意一个提交失败, 则后面的表单也不会进行提交了, 这就与咱们需求不符合.

    Promise.allSettledPromise.all类似, 其参数接受一个Promise的数组, 返回一个新的Promise, 唯一的不同在于, 其不会进行短路, 也就是说当Promise全部处理完成后我们可以拿到每个Promise的状态, 而不管其是否处理成功.

    下图说明promise.allsettle()是如何工作的

    Promise の all()、race()、および allSettled() メソッドを理解する

    6.1 Promise.allSettled() 例子

    这是Promise.allSettled() 使用方式快速演示示例

        Promise.allSettled([
          Promise.resolve(&#39;a&#39;),
          Promise.reject(&#39;b&#39;),
        ])
        .then(arr => assert.deepEqual(arr, [
          { status: &#39;fulfilled&#39;, value:  &#39;a&#39; },
          { status: &#39;rejected&#39;,  reason: &#39;b&#39; },
        ]));
    ログイン後にコピー

    6.2 Promise.allSettled() 较复杂点的例子

    这个示例类似于.map()Promise.all()示例(我们从其中借用了downloadText()函数):我们下载多个文本文件,这些文件的url存储在一个数组中。但是,这一次,咱们不希望在出现错误时停止,而是希望继续执行。Promise.allSettled()允许咱们这样做:

        const urls = [
          &#39;http://example.com/exists.txt&#39;,
          &#39;http://example.com/missing.txt&#39;,
        ];
        
        const result = Promise.allSettled(
          urls.map(u => downloadText(u)));
        result.then(
          arr => assert.deepEqual(
            arr,
            [
              {
                status: &#39;fulfilled&#39;,
                value: &#39;Hello!&#39;,
              },
              {
                status: &#39;rejected&#39;,
                reason: new Error(&#39;Not Found&#39;),
              },
            ]
        ));
    ログイン後にコピー

    6.3 Promise.allSettled() 的简化实现

    这是promise.allsettle()的简化实现(不执行安全检查)

        function allSettled(iterable) {
          return new Promise((resolve, reject) => {
            function addElementToResult(i, elem) {
              result[i] = elem;
              elementCount++;
              if (elementCount === result.length) {
                resolve(result);
              }
            }
        
            let index = 0;
            for (const promise of iterable) {
              // Capture the current value of `index`
              const currentIndex = index;
              promise.then(
                (value) => addElementToResult(
                  currentIndex, {
                    status: &#39;fulfilled&#39;,
                    value
                  }),
                (reason) => addElementToResult(
                  currentIndex, {
                    status: &#39;rejected&#39;,
                    reason
                  }));
              index++;
            }
            if (index === 0) {
              resolve([]);
              return;
            }
            let elementCount = 0;
            const result = new Array(index);
          });
        }
    ログイン後にコピー

    7. 短路特性

    Promise.all()romise.race() 都具有 短路特性

    • Promise.all(): 如果参数中 promise 有一个失败(rejected),此实例回调失败(reject)

    Promise.race():如果参数中某个promise解决或拒绝,返回的 promise就会解决或拒绝。

    8.并发性和 Promise.all()

    8.1 顺序执行与并发执行

    考虑下面的代码:

        asyncFunc1()
          .then(result1 => {
            assert.equal(result1, &#39;one&#39;);
            return asyncFunc2();
          })
          .then(result2 => {
            assert.equal(result2, &#39;two&#39;);
          });
    ログイン後にコピー

    使用.then()顺序执行基于Promise的函数:只有在 asyncFunc1()的结果被解决后才会执行asyncFunc2()

    Promise.all() 是并发执行的

        Promise.all([asyncFunc1(), asyncFunc2()])
          .then(arr => {
            assert.deepEqual(arr, [&#39;one&#39;, &#39;two&#39;]);
          });
    ログイン後にコピー

    8.2 并发技巧:关注操作何时开始

    确定并发异步代码的技巧:关注异步操作何时启动,而不是如何处理它们的Promises

    例如,下面的每个函数都同时执行asyncFunc1()asyncFunc2(),因为它们几乎同时启动。

        function concurrentAll() {
          return Promise.all([asyncFunc1(), asyncFunc2()]);
        }
        
        function concurrentThen() {
          const p1 = asyncFunc1();
          const p2 = asyncFunc2();
          return p1.then(r1 => p2.then(r2 => [r1, r2]));
        }
    ログイン後にコピー

    另一方面,以下两个函数依次执行asyncFunc1()asyncFunc2(): asyncFunc2()仅在asyncFunc1()的解决之后才调用。

        function sequentialThen() {
          return asyncFunc1()
            .then(r1 => asyncFunc2()
              .then(r2 => [r1, r2]));
        }
        
        function sequentialAll() {
          const p1 = asyncFunc1();
          const p2 = p1.then(() => asyncFunc2());
          return Promise.all([p1, p2]);
        }
    ログイン後にコピー

    8.3 Promise.all() 与 Fork-Join 分治编程

    Promise.all() 与并发模式“fork join”松散相关。重温一下咱们前面的一个例子:

        Promise.all([
            // (A) fork
            downloadText(&#39;http://example.com/first.txt&#39;),
            downloadText(&#39;http://example.com/second.txt&#39;),
          ])
          // (B) join
          .then(
            (arr) => assert.deepEqual(
              arr, [&#39;First!&#39;, &#39;Second!&#39;]
            ));
    ログイン後にコピー
    • Fork:在A行中,分割两个异步任务并同时执行它们。
    • Join:在B行中,对每个小任务得到的结果进行汇总。

    英文原文:https://2ality.com/2019/08/promise-combinators.html

    更多编程相关知识,请访问:编程教学!!

以上がPromise の all()、race()、および allSettled() メソッドを理解するの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

関連ラベル:
ソース:segmentfault.com
このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。
人気のチュートリアル
詳細>
最新のダウンロード
詳細>
ウェブエフェクト
公式サイト
サイト素材
フロントエンドテンプレート