繁体   English   中英

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

[英]Why is my array of Promises running before calling Promise.all()?

我正在尝试创建一个 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
});

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

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

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

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

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

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

所以,只要你运行这个:

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

您在got()异步操作已经开始,当它完成时,它将通过承诺将其传达回来。

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() ,您只是要求监视承诺数组,以便它会告诉您何时有被拒绝的承诺或所有承诺何时成功完成。

请注意,使用 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 点有点复杂,可以使用这个辅助函数进行节流,看起来像这样:

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

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

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

鉴于所有这些,将 Promise 视为“开始”或“运行”的东西有点奇怪。 它们只是在挂起状态下创建。 剩下的事情是来自某个 API 的挂起回调,它有望发生,然后将更改承诺对象的状态, then触发回调。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM