Promises 在定义时执行而不是 Promises.all?

Posted

技术标签:

【中文标题】Promises 在定义时执行而不是 Promises.all?【英文标题】:Promises execute when defined instead of Promises.all? 【发布时间】:2021-06-07 03:44:27 【问题描述】:

我正在尝试创建一个 promise 数组,以便我可以批量处理 promise 而不是一起执行它们以避免关闭我的服务器。我想一次执行最多 20 个承诺。 我写了以下代码:

let promises = [];
let create;
let update;
let i=0;
let users = users.json;

if (users && usersjson.users.length) 
  for (const user of users) 
    if (userexists) 
      update = new promise(async (resolve,reject) => 
        //Function to update user
      );
      promises.push(update);
      i++;
     else 
      create = new promise (async (resolve,reject) => 
        //Function to create a new user
      );
      promises.push(update);
      i++;
    
  if (promises.length >= 20 || i >= usersjson.users.length) 
    await Promise.all(promises)
    .then((response)=>
    console.log(response);
    promises=[];
  ).catch((err)=> 
    console.log(err);
  )



但是,我发现promise是在我定义它们的时候执行的,而不是在我调用Promise.all时被压入数组并执行,我不知道为什么。

我还希望 Promise.all 函数继续运行,即使单个 Promise 被拒绝,如果我的代码构建方式可行的话。如果代码失败,我在每个承诺中都有一个问题,这是正确的方法吗?

【问题讨论】:

Promise 不是稍后触发的东西。一个promise只是标记已经有一个操作正在运行并且一个值挂起。它作为值可用时的通知机制。 所以为了做到这一点,我需要更改所有对函数的承诺并将它们推送到数组中? Promise 不会在你“定义”它们时执行,也不会在你调用Promise.all() 时执行,但是当它们从执行堆栈中取出时它们会被执行。如果你不想拒绝Promise.all() 拒绝,当一个承诺被拒绝时,你将不得不抓住每个承诺的拒绝。但是当然你必须检查结果数组 我看到它多次提到,所以为了消除混淆 - 承诺没有“执行”。执行一个异步任务返回一个promise。承诺不是任务本身,它不是“运行”或类似的东西。当您订购比萨饼并获得收据时,制作比萨饼的不是收据。您的披萨是由其他人在其他地方制作的 - 收据只是确认您的订单已被接受。当您的号码在比萨制作过程结束时被呼叫时,这就是承诺通知机制。你可以.then(pizza => goHomeWith(pizza)).then(eat) 【参考方案1】:

如 cmets 中所述,promise 对象实际上在您执行异步操作后立即进入“待处理”状态,并在操作完成后立即“完成”。

因此,在您的代码中,您实际上为所有用户创建并运行异步操作,而不仅仅是其中的 20 个。

有两种解决方案适合您。主要的一个是创建返回一个 Promise 并且一次只运行 20 个的函数。

const jobs = users.map(user => () => user.update(...)); // array of functions
while (jobs.length > 0) 
   await Promise.all(jobs.splice(0,20).map(job => job())); // take 20 and process

另一种解决方案是使用像 bluebird 这样的库,它有很多有用的方法来处理 Promise。您可能会喜欢map(),它支持concurrency limit。


您的第二个问题是关于Promise.all 中的错误导致整个系列失败的原因。 为了防止您可以将.catch() 添加到每个作业,例如,在那里返回null 或任何其他值,这将帮助您确定某些操作失败。当然,这种做法也会防止Promise.all打断。

const jobs = users.map(user => () => user.update().catch(e => null));

【讨论】:

谢谢!我不确定我是否了解地图功能的工作原理。我阅读了文档,但它看起来与您编写它的方式不同。 在我的示例中,map 是一个简单的Array.map,我在用户数组上使用它以获得新的函数数组。 Bluebirds 地图是另一回事,您可能需要花更多时间阅读 bluebird 文档才能理解它。祝你好运! 感谢@Artem,但我仍然很困惑。我在这个新的“工作”数组中添加了哪些函数?我没有将功能推向“承诺”,而是将它们推向“工作”?为什么我需要将它附加到用户元素? 这只是一个例子。主要思想是创建返回承诺的函数数组。在我的示例中,我假设 user 对象具有返回承诺的 update 方法,这使示例更短且更具可读性。在您的情况下,它可能会以许多其他方式呈现 所以在我的示例中,我将使用“usersjson.users.map()”而不是执行循环“for (const user of usersjson.users)”,然后将函数放入地图功能?如果我需要添加一个后面也有“.then”的函数会怎样?【参考方案2】:

javascript 中的函数直接执行。与 Promise 的不同之处在于您可以以 Promise 风格的方式等待它。当函数被调用时,就像你在 for 循环中所做的那样,它会立即执行它。稍后,使用Promise.all,您只是说:“等待我在数组中定义的承诺完成”(如果其中一个失败,则会出错)。


关于您的第二个问题,有一种方法可以等待所有承诺完成,而不是抛出错误(从而停止)。那是Promise.allSettled。顾名思义,它会等待所有的承诺达成。

但是,请记住,响应将不同于 Promise.all 函数。 As stated in the docs,如果函数被“完成”或“拒绝”,它将为每个承诺返回一个数组。

【讨论】:

以上是关于Promises 在定义时执行而不是 Promises.all?的主要内容,如果未能解决你的问题,请参考以下文章

避免使用promises中的内存泄漏并在咖啡脚本中循环(无需等待)

Node.js:何时使用 Promises 与 Callbacks

使用 Promises 的代码根本不执行

ES6 Promises - 类似 async.each 的东西?

如何在Node.js中对使用promises和事件发射器的函数进行单元测试?

(mongoose/promises) 如何检查文档是不是是使用 findOneAndUpdate 和 upsert 创建的