为什么我的Promise数组在调用Promise.all()之前运行?

时间:2017-12-03 19:25:35

标签: javascript promise got

我试图创建一个Promises数组,然后使用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被标记为" pending"当我将它们添加到数组中时,这意味着它们已经开始了。

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

这是否意味着我要两次开始?

4 个答案:

答案 0 :(得分:12)

你没有两次开始。 Prom一旦创建就开始运行 - 或者只要JS引擎找到足够的资源来启动它们。你无法控制他们何时真正开始。

所有Promise.all()所做的就是等待所有人解决(解决或拒绝)。 Promise.all()不会干扰或影响承诺本身的执行顺序/时间。

答案 1 :(得分:7)

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

所以,只要你跑了这个:

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

got()内部的异步操作已经开始,一旦完成,它将通过承诺进行通信。

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

如果你将代码分解成碎片,这就是每件作品中发生的事情:

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":{}}
}

这个循环,只是查看所有的promises,看看是否有任何一个可能已经被解析,尽管人们不会指望它们是因为它们的基础异步操作刚刚在前一个循环中启动。

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

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

答案 2 :(得分:1)

Promises在创建时“启动”,即为您提供承诺的函数,已经启动了(通常是异步的)操作,最终会导致异步结果。例如,如果函数返回HTTP请求结果的承诺,则在返回promise对象时它已经启动了该HTTP请求。

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

考虑到所有这些,将承诺视为“开始”或“运行”的事情有点奇怪。它们仅在待定状态下创建。剩下的事情是来自某些API的挂起回调,希望会发生,然后会改变promise对象的状态,触发then回调。

答案 3 :(得分:1)

请注意,使用Promise.all获取网址数组会遇到一些问题:

  1. 如果任何网址无法获取,您的决心永远不会被调用(所以 一个失败,你的解析函数永远不会被调用。
  2. 如果你的阵列非常大,你会在网站和你的网络上发出请求,你可能想要限制在一定时间内完成的最大开放请求和/或请求。
  3. 第一个问题很容易解决,您处理失败的请求并将其添加到结果中。在解析处理程序中,您可以决定如何处理失败的请求:

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