何时使用promise.all()

When to use promise.all()?

本文关键字:all promise 何时使      更新时间:2023-09-26

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

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

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

我唯一能想到的是:

  • 所有的promise对象都解析并拒绝时,只解析使用promise.all()

我不确定是否有人真正给出了最通用的解释,什么时候使用Promise.all()(什么时候不使用它):

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

Promise.all()在你有多个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.all(),这种类型的实现需要适合您的情况。在其他情况下,您需要运行多个异步操作,并且需要所有结果,即使其中一些失败。Promise.all()不会直接为您做这些。相反,您可能会在这种情况下使用Promise.settle()之类的东西。您可以在这里看到.settle()的实现,它使您可以访问所有结果,即使有些结果失败。当您预计某些操作可能会失败,并且您有一个有用的任务要处理任何操作成功的结果时,或者您想要检查所有失败的操作的失败原因并据此做出决策时,这一点特别有用。

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

如上所述,单独的异步操作是什么或者它们是否是相同的类型并不重要。重要的是你的代码是否需要协调它们,并知道它们何时都成功。


当你想而不是使用Promise.all():

时,列出一些情况也很有用。
  1. 当只有一个异步操作时。只有一个操作,你可以只对一个承诺使用.then()处理程序,没有理由使用Promise.all()
  2. 当您不需要在多个异步操作之间进行协调时。
  3. 当快速失败实现不合适时。如果你需要所有的结果,即使有些失败了,那么Promise.all()本身不会这样做。你可能会想要Promise.allSettled()之类的东西。
  4. 如果你的异步操作不返回承诺,Promise.all()不能跟踪一个不通过承诺管理的异步操作。

Promise.all用于等待多个promise并行(同时)解决。它返回一个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.all可以和Promise实例一起使用。

很难回答这些问题,因为当使用语言特性的可用api时,这些问题往往会自己回答。基本上,只要你避免使用它们的反模式,就可以以任何适合你用例的方式使用promise。

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

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

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

一般为no和no。

我使用promise.all()当我必须做一些请求我的API,我不想在应用程序加载所有请求的数据之前显示的东西,所以我延迟执行流,直到我有我需要的所有数据。

的例子:

我想做什么我想加载我的应用程序的用户和他们的产品(想象一下,你必须做多个请求)之前,在我的应用程序中显示一个表与用户的电子邮件和每个用户的产品名称。

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

我做什么当所有的数据已经加载一旦数据到达我的应用程序,我可以执行promises.all()的回调,然后使表与用户可见。

我希望它能帮助您了解在哪种情况下使用promises.all()

正如@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();

我倾向于在以下情况下使用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.all()基本上会把我所有的承诺聚合成一个,我可以通过下一个then来管理它。我可以一直这样做,只要一个喜欢,说每个配置文件,我想得到相关的设置等。每次我创造更多的承诺时,我都可以将它们集合成一个。

承诺。all-这个方法在你想要等待多个承诺完成时很有用,或者promise .all(iterable)方法返回一个承诺,当iterable参数中的所有承诺都解决时,该承诺就会解决,或者因为第一个传递的承诺被拒绝而拒绝。

2。只需使用Promise.all(files)。Catch (err => {})如果任何一个承诺被拒绝,将抛出一个错误。

3。如果你想等待所有的一切,就在所有的承诺之前反思一下拒绝或履行承诺

  • 语法-Promise.all (iterable);
  • 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

    仅当您需要根据使用promise的多个异步操作的结果运行代码时才使用Promise.all

    例如

    :您有这样一个场景,您需要从服务器下载2000mb的文件,同时您要释放用户存储空间,以确保它可以保存下载的文件。只有当文件下载成功,存储空间创建成功时才需要保存。你要这样做。
    第一个异步操作

    var p1 = new Promise(function(resolve, reject) {
      // you need to download 2000mb file and return resolve if 
      // you successfully downloaded the file 
    })
    

    和第二个异步操作

    var p2 = new Promise(function(resolve, reject) {
      // you need to clear the user storage for 2000 mb
      // which can take some time 
    })
    

    现在您只希望在两个承诺都成功解决时保存,否则不保存。你将使用promise。都是这样。

    Promise.all([p1,p2]).then((result)=>{
      // you will be here only if your both p1 and p2 are resolved successfully.
      // you code to save the downloaded file here
    })
    .catch((error)=>{
      // you will be here if at-least one promise in p1,p2 is rejected. 
      // show error to user 
      // take some other action
    })
    

    承诺。当有一个基于特定标准验证多个规则的例程,并且必须并行执行它们并需要在一个点上查看这些规则的结果时,所有这些都可以使用。的承诺。All将结果作为在规则验证程序中解析的数组返回。

    const results = await Promise.all([validateRule1, validateRule2, validateRule3,  ...]);
    

    results数组可能看起来像(取决于条件)例如:[true, false, false]

    现在您可以拒绝/接受基于返回值的结果。使用这种方式,您将不必对if-then-else应用多个条件。

    如果你感兴趣只承诺。

    Promise(通常被称为"Promise")——提供一种方便的方式来组织异步代码。

    Promise -是一个包含状态的特殊对象。最初,等待(«等待"),然后-一个:完成(«成功")或拒绝(«错误完成")。

    承诺挂起的回调函数可以有两种类型:

    • 未完成-当承诺处于"完成"状态时触发成功。"
    • 拒绝-当承诺在"出错"时触发。
    创建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)
    
    • oncompleted -一个将与结果一起调用的函数解决。

    • 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 诺言化——当采用异步功能并使其成为返回诺言的包装器时。

    在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)

    叫承诺。all (iterable)接收一个数组(或其他可迭代对象)并返回一个PROMIS,它等待所有传输的PROMIS完成,然后用一个结果数组将状态变为"done"。

    例如:

    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. 创建每个URL对应的PROMIS
    2. 在Promise.all中包装一个promise数组

    我们得到:

    使用严格的;

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

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

    的承诺。所有这些错误。

    同时忽略其余的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)成功,拒绝(错误)-错误。
    • 参数resolve/reject(只有第一个,其余的被忽略)
    • 通过调用.then/catch来指定处理程序。
    • 使用Channing将结果从一个处理器传输到另一个处理器。
    https://www.promisejs.org/patterns/