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

问题描述

我正在尝试创建一系列承诺,以便我可以批量承诺而不是同时执行它们以避免关闭我的服务器。我想一次最多执行 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的时候被push进数组执行,我也想不通。

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

解决方法

正如评论中提到的,promise 对象实际上在您执行异步操作后立即进入“待处理”状态,并在操作完成后立即“完成”。

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

有两种解决方案。主要的一个是创建返回一个 promises 并且一次只运行 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));
,

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


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

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