【问题标题】:When to use promise.all()?何时使用 promise.all()?
【发布时间】:2016-11-05 21:53:36
【问题描述】:

这更像是一个概念性问题。我了解 Promise 设计模式,但找不到可靠的来源来回答我关于 promise.all() 的问题:

什么是正确的使用场景promise.all()

是否有使用promise.all() 的最佳实践?只有当所有的 Promise 对象都是相同或相似的类型时才应该理想地使用它?

我唯一能想到的是:

  • 如果您想所有的 Promise 对象解析时使用 promise.all() 并在即使有一个拒绝时也拒绝。

【问题讨论】:

    标签: javascript design-patterns promise


    【解决方案1】:

    很难回答这些问题,因为当人们使用语言功能的可用 API 时,它们往往会自行回答。基本上,只要您避免使用它们的anti-patterns,就可以以任何适合您用例的方式使用 Promises。

    什么是使用 promise.all() 的正确场景

    操作依赖于多个promise的成功解决的任何情况。

    有没有使用 promise.all() 的最佳实践?是否应该仅在所有 promise 对象的类型相同或相似时才使用它?

    一般来说,没有也没有。

    【讨论】:

      【解决方案2】:

      当我必须对我的 API 进行一些请求并且我不想在应用程序加载所有请求的数据之前显示某些内容时,我会使用 promise.all(),因此我会延迟执行流程,直到获得所需的所有数据.

      例子:

      我想要做什么我想在我的应用中显示一个包含用户电子邮件和每个用户的产品名称。

      下一步做什么我将请求发送到我的 API,创建承诺并使用 promise.all()

      加载完所有数据后我会做什么一旦数据到达我的应用程序,我可以执行promises.all()的回调,然后让用户可以看到表格。

      我希望它可以帮助您了解在哪种情况下使用promises.all() 是有意义的

      【讨论】:

        【解决方案3】:

        Promise.all-当您想要等待多个 Promise 完成或 Promise.all(iterable) 方法返回一个当 iterable 参数中的所有 Promise 都已解决时解决的 Promise,此方法很有用,或以第一个被拒绝的承诺的原因拒绝。

        2.只需使用 Promise.all(files).catch(err => { }) 如果任何承诺被拒绝,这将引发错误。

        3.如果你想等待所有,在 .all 之前使用 .reflect 承诺拒绝或履行

        1. 语法 -Promise.all(iterable);

        【讨论】:

        • 使用 Promise.all- var p1 = Promise.resolve(3) 的示例;变量 p2 = 1000; var p3 = new Promise((resolve, reject) => { setTimeout(resolve, 100, "genious"); }); Promise.all([p1, p2, p3]).then(values => { console.log(values); // [3, 1000, "genious"] });
        【解决方案4】:

        我倾向于将 promise all 用于这样的事情:

        myService.getUsers()
           .then(users => {
               this.users = users;
        
               var profileRequests = users.map(user => {
                   return myService.getProfile(user.Id); // returns a promise
               });
        
               return Promise.all(profileRequests);
           })
           .then(userProfilesRequest => {
               // do something here with all the user profiles, like assign them back to the users.
        
               this.users.forEach((user, index) => {
                   user.profile = userProfilesRequest[index];
               });
           });
        

        在这里,我们将为每个用户获取他们的个人资料。既然我有 x 数量的 Promise 需要解决,我不希望我的 Promise 链失控。

        所以Promise.all() 基本上会将我所有的承诺归为一个,我可以通过下一个then 来管理它。只要喜欢,我就可以一直这样做,比如对于每个配置文件我想获得相关设置等等。每次我创建更多的承诺时,我可以将它们全部聚合成一个。

        【讨论】:

          【解决方案5】:

          Promise.all 用于等待多个 Promise 并行解决(同时)。它返回一个 Promise,当所有输入的 Promise 都已解析时,它会解析:

          // p1, p2, p3 are Promises
          Promise.all([p1, p2, p3])
            .then(([p1Result, p2Result, p3Result]) => {
              // This function is called when p1, p2 and p3 have all resolved.
              // The arguments are the resolved values.
            })
          

          如果任何个输入Promise被拒绝,Promise.all返回的Promise也被拒绝。

          一个常见的场景是等待多个 API 请求完成,以便您可以组合它们的结果:

           const contentPromise = requestUser();
           const commentsPromise = requestComments();
          
           const combinedContent = Promise.all([contentPromise, commentsPromise])
             .then(([content, comments]) => {
               // content and comments have both finished loading.
             })
          

          您可以将Promise.allPromise 实例一起使用。

          【讨论】:

            【解决方案6】:

            Promise.all 从它所传递的可迭代对象中的所有承诺中传递一个值数组。

            https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Promise/all

            var isCallFailed = false;
            function myEndpoint1() {
              return isCallFailed ? Promise.reject("Bohoo!") :Promise.resolve({"a":"a"});
            }
            function myEndpoint2() {
              return Promise.resolve({"b":"b"});
            }
            
            Promise.all([myEndpoint1(), myEndpoint2()])
            .then(values => {
              var data1 = values[0];
              var data2 = values[1];
              alert("SUCCESS... data1: " + JSON.stringify(data1) + "; data2: " +  JSON.stringify(data2));
            })
            .catch(error => {
              alert("ERROR... " + error);
            });
            

            您可以通过isCallFailed = true 尝试另一个案例。

            【讨论】:

              【解决方案7】:

              如果您只对 Promise.all 感兴趣,请阅读下面的 Promise.all

              Promise(通常称为“Promise”)- 提供一种方便的方式来组织异步代码。

              Promise - 是一个包含您的状态的特殊对象。最初,待处理(“等待”),然后是以下之一:已完成(“成功”)或被拒绝(“错误完成”)。

              关于挂起回调的承诺可以有两种:

              • unFulfilled - 当 Promise 处于“完成”状态时触发 成功。”
              • Rejected - 当 promise 在“made in error”时触发。

              创建 Promise 的语法:

              var promise = new Promise(function(resolve, reject) {
                // This function will be called automatically
              
                // It is possible to make any asynchronous operations,
                // And when they will end - you need to call one of:
                // resolve(result) on success
                // reject(error) on error
              })
              

              挂起处理程序的通用方法:

              promise.then(onFulfilled, onRejected)
              
              • onFulfilled - 一个函数,将使用结果调用 解决。

              • onRejected - 错误拒绝时将调用的函数。

              在它的帮助下,您可以同时分配一个处理程序,并且只分配一个:

              // onFulfilled It works on success
              promise.then(onFulfilled)
              // onRejected It works on error
              promise.then(null, onRejected)
              

              同步抛出 - 与拒绝相同

              'use strict';
              
              let p = new Promise((resolve, reject) => {
                // то же что reject(new Error("o_O"))
                throw new Error("o_O");
              });
              
              p.catch(alert); // Error: o_O
              

              承诺 Promisification - 当采用异步功能并使其成为返回 PROMIS 的包装器时。

              Promisification 之后函数的使用通常会变得更加方便。

              例如,为使用 XMLHttpRequest 请求制作一个包装器

              httpGet 函数 (url) 将返回 PROMIS,在成功加载数据后,该 url 将与这些数据一起完成,如果出现错误 - 将被拒绝并显示错误信息:

              function httpGet(url) {
              
                  return new Promise(function(resolve, reject) {
              
                      var xhr = new XMLHttpRequest();
                      xhr.open('GET', url, true);
              
                      xhr.onload = function() {
                          if (this.status == 200) {
                              resolve(this.response);
                          } else {
                              var error = new Error(this.statusText);
                              error.code = this.status;
                              reject(error);
                          }
                      };
              
                      xhr.onerror = function() {
                          reject(new Error("Network Error"));
                      };
              
                      xhr.send();
                  });
              
              }
              

              如您所见,在函数内部 XMLHttpRequest 对象像往常一样被创建和发送,当 onload / onerror 被分别调用时,分别解析(状态为 200)或拒绝。

              使用:

              httpGet("/article/promise/user.json")
                  .then(
                      response => alert(`Fulfilled: ${response}`),
                      error => alert(`Rejected: ${error}`)
                  );
              

              并行执行

              如果我们想同时实现多个异步进程并处理它们的结果怎么办?

              Promise 类具有以下静态方法。

              Promise.all(iterable)

              调用 Promise.all (iterable) 接收一个数组(或其他可迭代对象)并返回 PROMIS PROMIS,它会等待直到所有传输的 PROMIS 完成,并通过结果数组更改为“完成”状态。

              例如:

              Promise.all([
                  httpGet('/article/promise/user.json'),
                  httpGet('/article/promise/guest.json')
              ]).then(results => {
                  alert(results);
              });
              

              假设我们有一个 URL 数组。

              let urls = [
                '/article/promise/user.json',
                '/article/promise/guest.json'
              ];
              

              要并行下载它们,您需要:

              1. 为每个与 PROMIS 对应的 URL 创建。
              2. 在 Promise.all 中包装一个 PROMIS 数组。

              我们得到这个:

              '使用严格';

              let urls = [
                '/article/promise/user.json',
                '/article/promise/guest.json'
              ];
              
              Promise.all( urls.map(httpGet) )
                .then(results => {
                  alert(results);
              });
              

              请注意,如果任何 Promise 以错误结束,结果将

              Promise.all 这个错误。

              同时忽略 PROMIS 的其余部分。

              例如:

              Promise.all([
                  httpGet('/article/promise/user.json'),
                  httpGet('/article/promise/guest.json'),
                  httpGet('/article/promise/no-such-page.json') // (нет такой страницы)
              ]).then(
                  result => alert("не сработает"),
                  error => alert("Ошибка: " + error.message) // Ошибка: Not Found
              )
              

              总共:

              • Promise - 是一个特殊的对象,用于存储其状态,即当前 结果(如果有)和回调。
              • 当你创建一个新的 Promise ((resolve, reject) => ...) 函数时 参数自动启动,它应该调用 resolve (result) on 成功,然后拒绝(错误) - 错误。
              • 参数解析/拒绝(仅第一个,其余的被忽略) 在这个 Promise 上传递给处理程序。
              • 通过调用 .then / catch 指定处理程序。
              • 使用 Channing 将结果从一个处理器传输到另一个处理器。

              https://www.promisejs.org/patterns/

              【讨论】:

              • 您似乎只是对 Promises 进行了一般性介绍,甚至没有提到 Promise.all ......这就是问题所要问的。
              • 您对我分批输入文字表示赞赏。因为当我尝试输入文字的重量时,我遇到了问题
              • 希望专家们能欣赏我的回答
              【解决方案8】:

              我不确定是否有人真的就何时使用Promise.all()(以及何时不使用它)给出了最通用的解释:

              什么是使用 promise.all() 的正确场景

              Promise.all() 在您有多个 Promise 并且您的代码想知道这些 Promise 所代表的所有操作何时成功完成时很有用。各个异步操作是什么并不重要。如果它们是异步的,由 Promise 表示,并且您的代码想知道它们何时全部成功完成,那么 Promise.all() 就是为此而构建的。

              例如,假设您需要从三个单独的远程 API 调用中收集信息,并且当您获得所有三个 API 调用的结果时,您需要使用所有三个结果运行一些进一步的代码。这种情况对于Promise.all() 来说是完美的。你可以这样:

              Promise.all([apiRequest(...), apiRequest(...), apiRequest(...)]).then(function(results) {
                  // API results in the results array here
                  // processing can continue using the results of all three API requests
              }, function(err) {
                  // an error occurred, process the error here
              });
              

              Promise.all() 可能最常用于类似类型的请求(如上例所示),但没有理由需要这样做。如果您有不同的情况,您需要发出远程 API 请求、读取本地文件并读取本地温度探测器,然后当您拥有来自所有三个异步操作的数据时,您想要对来自所有三个异步操作的数据进行一些处理三、你会再次使用Promise.all()

              Promise.all([apiRequest(...), fs.promises.readFile(...), readTemperature(...)]).then(function(results) {
                  // all results in the results array here
                  // processing can continue using the results of all three async operations
              }, function(err) {
                  // an error occurred, process the error here
              });
              

              另一方面,如果您不需要在它们之间进行协调并且可以单独处理每个异步操作,那么您不需要Promise.all()。您可以使用自己的 .then() 处理程序触发每个单独的异步操作,并且不需要它们之间的协调。

              此外,Promise.all() 具有所谓的“快速失败”实现。它返回一个主 Promise,一旦你传递的第一个 Promise 被拒绝,它就会被拒绝,或者当所有的 Promise 都解决后它会解决。因此,要使用Promise.all(),这种类型的实现需要适合您的情况。在其他情况下,您想要运行多个异步操作并且需要所有结果,即使其中一些失败。 Promise.all() 不会直接为您执行此操作。相反,您可能会在这种情况下使用 Promise.settle() 之类的东西。您可以看到an implementation of .settle() here,它可以让您访问所有结果,即使有些结果失败了。如果您预计某些操作可能会失败,并且您有一项有用的任务需要处理任何成功操作的结果,或者您想要检查所有未能根据此做出决策的操作的失败原因,这尤其有用。

              有没有使用 promise.all() 的最佳实践?应该是 理想情况下,仅当所有承诺对象都相同或 类似的类型?

              如上所述,各个异步操作是什么或它们是否为同一类型并不重要。重要的是您的代码是否需要协调它们并知道它们何时都成功。


              列出一些使用Promise.all()的情况也很有用:

              1. 当您只有一个异步操作时。只需一个操作,您就可以在一个 Promise 上使用 .then() 处理程序,而 Promise.all() 没有任何理由。
              2. 当您不需要在多个异步操作之间进行协调时。
              3. 当快速失败实施不合适时。如果您需要所有结果,即使有些结果失败,那么 Promise.all() 将不会自行完成。您可能会想要 Promise.allSettled() 之类的东西。
              4. 如果您的异步操作并非全部返回 Promise,Promise.all() 无法跟踪未通过 Promise 管理的异步操作。

              【讨论】:

              • 这句话让我明白了一切。如果要执行传递从多个 ajax 调用获得的参数的函数。然后承诺一切都是正确的做法。
              • maaan 这个答案真是太棒了!你教书还是什么的?因为我很乐意参加!
              【解决方案9】:

              正如@joews 提到的,Promise.all 的重要特性之一可能应该明确指出是它使您的异步代码更快

              这使得它非常适用于任何包含独立调用的代码(我们希望在其余代码继续之前返回/完成),尤其是当我们进行前端调用并希望用户体验尽可能流畅时。

              async function waitSecond() {
                return new Promise((res, rej) => {
                  setTimeout(res, 1000);
                });
              }
              
              function runSeries() {
                console.time('series');
                waitSecond().then(() => {
                  waitSecond().then(() => {
                    waitSecond().then(() => {
                      console.timeEnd('series');
                    });
                  });
                });
              }
              
              function runParallel() {
                console.time('parallel');
                Promise.all([
                  waitSecond(),
                  waitSecond(),
                  waitSecond(),
                ]).then(() => {
                  console.timeEnd('parallel');
                });
              }
              
              runSeries();
              runParallel();

              【讨论】:

                猜你喜欢
                • 2019-06-07
                • 1970-01-01
                • 1970-01-01
                • 2018-12-26
                • 2023-03-19
                • 2021-04-13
                • 2021-11-01
                • 2018-05-05
                相关资源
                最近更新 更多