【问题标题】:Why is my array of Promises running before calling Promise.all()?为什么我的 Promise 数组在调用 Promise.all() 之前运行?
【发布时间】:2017-12-03 19:25:35
【问题描述】:

我正在尝试创建一个 Promise 数组,然后使用 Promise.all() 解决它们。我正在使用 got,它返回一个承诺。

我的代码可以工作,但我不完全理解如何工作。这里是:

const got = require('got');

const url = 'myUrl';
const params = ['param1', 'param2', 'param3'];

let promiseArray = [];
for (param of params) {
    promiseArray.push(got(url + param));
}

// Inspect the promises
for (promise of promiseArray) {
    console.log(JSON.stringify(promise));
    // Output: promise: {"_pending":true,"_canceled":false,"_promise":{}}
}

Promise.all(promiseArray).then((results) => {
     // Operate on results - works just fine
}).catch((e) => {
    // Error handling logic
});

让我失望的是,当我将 Promises 添加到数组中时,它们被标记为“待处理”,这意味着它们已经开始了。

我认为它们应该在promiseArray 中处于非活动状态,而Promise.all(promiseArray) 将启动它们并解决它们。

这是否意味着我要启动它们两次?

【问题讨论】:

    标签: javascript promise


    【解决方案1】:

    您不会启动它们两次。 Promise 在创建后立即开始运行——或者一旦 JS 引擎找到足够的资源来启动它们。您无法控制它们何时真正开始。

    所有Promise.all() 所做的只是等待它们全部解决(解决或拒绝)。 Promise.all() 不会干扰或影响 promise 本身的执行顺序/时间。

    【讨论】:

    • 假设我没有调用 Promise.all(),这是否意味着 Promise 会在 promiseArray 中自行解决/失败?所以过了一会儿,我会有一系列已解决/失败的 Promise?
    • 没错!每个 Promise 将只运行一次,然后保持其最终状态(已解决或已拒绝),只要您仍然有 Promise 对象在范围内或关闭它,您就可以在将来随时检查它。
    • @alexcs - 这个答案非常具有误导性。 Promise 不会“运行”,也不会“开始”。它们只是坐在那里不做任何事情的对象,直到其他一些异步操作通过调用resolve()reject() 来告诉它们做某事。承诺只是一个通知系统。请参阅我的答案,以获取有关事物实际工作原理的更技术正确的解释。
    • @jfriend00 我认为通俗地说,我们指的是promise所代表的异步操作。即“promise 所代表的异步操作”开始运行...
    • @ArashMotamedi - 我认为 OP 根本不“理解”这一点。他们显然对什么运行,什么不运行以及承诺实际上是什么感到非常困惑。弄清楚这一点是回答和清楚实际发生的事情的重要部分。
    【解决方案2】:

    Promise 根本没有运行。它们只是一个通知系统,用于在异步操作完成时进行通信。

    所以,只要你运行这个:

    promiseArray.push(got(url + param));
    

    got() 内部的异步操作已经开始,当它完成时,它将通过 Promise 将其传达回来。

    Promise.all() 所做的只是监控所有的承诺,并在第一个拒绝或所有承诺都成功完成时告诉你。它不会以任何方式“控制”异步操作。相反,您启动异步操作,它们通过 Promise 进行通信。您可以控制何时开始异步操作,然后异步操作从那时起自行运行。


    如果你把你的代码分成几块,下面是每一块发生的事情:

    let promiseArray = [];
    for (param of params) {
        promiseArray.push(got(url + param));
    }
    

    这会多次调用got(),启动该函数中的任何异步操作。 got() 可能会返回一个 promise 对象,然后将其放入您的 promiseArray。所以,此时,异步操作都已经启动并自行运行。

    // Inspect the promises
    for (promise of promiseArray) {
        console.log(JSON.stringify(promise));
        // Output: promise: {"_pending":true,"_canceled":false,"_promise":{}}
    }
    

    这个循环,只是查看所有的 Promise,看看它们中的任何一个是否已经被解决,尽管人们不希望它们是,因为它们的底层异步操作刚刚在前一个循环中开始。

    Promise.all(promiseArray).then((results) => {
         // Operate on results - works just fine
    }).catch((e) => {
        // Error handling logic
    });
    

    然后,使用Promise.all(),您只是要求监控一组承诺,以便它会告诉您何时有被拒绝的承诺或所有承诺何时成功完成。

    【讨论】:

      【解决方案3】:

      Promise 在创建时“开始”,即为您提供 Promise 的函数,已经启动了(通常是异步的)操作,最终将导致异步结果。例如,如果一个函数返回一个 HTTP 请求结果的 Promise,那么它在返回 Promise 对象时已经启动了该 HTTP 请求。

      无论您对该承诺对象做什么或不做什么,该函数 (got) 已经创建了一个回调函数,并将其传递给异步 API,例如 HTTP 请求/响应 API。在该回调函数中(除非您检查got 的来源,否则您看不到该回调函数),一旦被该API 回调,promise 就会被解析。在 HTTP 请求示例中,API 使用 HTTP 响应调用该特定回调,然后所述回调函数解析承诺。

      鉴于这一切,将 Promise 视为“开始”或“运行”的东西有点奇怪。它们只是在挂起状态下创建的。剩下的就是来自某个 API 的待处理回调,它有望发生,然后会改变 Promise 对象的状态,触发 then 回调。

      【讨论】:

        【解决方案4】:

        请注意,使用 Promise.all 获取 url 数组可能会出现一些问题:

        1. 如果任何 url 未能获取您的解析,则永远不会调用(所以 一个失败,你的解析函数永远不会被调用。
        2. 如果您的阵列非常大,您会因请求而破坏站点和网络,您可能希望限制在特定时间范围内发出的最大打开请求数和/或请求数。

        第一个问题很容易解决,您处理失败的请求并将它们添加到结果中。在解析处理程序中,您可以决定如何处理失败的请求:

        const got = require('got');
        
        const url = 'myUrl';
        const params = ['param1', 'param2', 'param3'];
        
        const Fail = function(details){this.details = details;};
        Promise.all(
          params.map(
            param =>
              got(url + param)
              .then(
                x=>x,//if resolved just pass along the value
                reject=>new Fail([reject,url+param])
              )
          )
        ).then((results) => {
          const successes = results.filter(result=>(result && result.constructor)!==Fail),
          const failedItems = results.filter(result=>(result && result.constructor)===Fail);
        }).catch((e) => {
            // Error handling logic
        });
        

        第 2 点有点复杂,可以使用this helper function 进行限制,看起来像这样:

        ... other code
        const max5 = throttle(5);
        Promise.all(
          params.map(
            param =>
              max5(got)(url + param)
              .then(
                x=>x,//if resulved just pass along the value
                reject=>new Fail([reject,url+param])
              )
          )
        )
        

        【讨论】:

        • 谢谢,第一个问题实际上是我必须尽快优化的问题。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-12-27
        • 2020-02-08
        • 2018-04-05
        • 2016-05-04
        • 2020-07-13
        • 2023-01-27
        相关资源
        最近更新 更多