如何等待所有 Promise 完成

How to wait for all Promises to be finished

我喜欢 运行 一组并行的功能,当所有功能都完成后,我喜欢 运行 一些最后的东西。我的 JavaScript 看起来像这样:

const logger = console;

const functionOne = function () {
  logger.info("Starting functionOne");
  return new Promise(resolve => {
    setTimeout(function () {
      logger.info("Finished functionOne after 20 sec.");
    }, 20000);
  });
};
    
const functionTwo = function () {
  logger.info("Starting functionTwo");
  return new Promise(resolve => {
    setTimeout(function () {
      logger.info("Finished functionTwo after 10 sec.");
    }, 10000);
  });
};

const runningFunctions = async function () {
  logger.info('Start jobs');
  functionOne();
  functionTwo();
}

runningFunctions();
logger.info(`All done after 20 sec.`);

原则上我的日志输出应该是这样的:

2021-11-24 16:54:31.111 [info] -> Start jobs
2021-11-24 16:54:31.112 [info] -> Starting functionOne
2021-11-24 16:54:31.113 [info] -> Starting functionTwo
2021-11-24 16:54:41.115 [info] -> Finished functionTwo after 10 sec.
2021-11-24 16:54:51.115 [info] -> Finished functionOne after 20 sec.
2021-11-24 16:54:51.116 [info] -> All done after 20 sec.

我尝试了不同的解决方案,例如

runningFunctions().then(
  () => { logger.info(`All done after 20 sec.`) }
).catch(
  err => { logger.error(err) }
);

Promise.all([functionOne(), functionTwo()]).then(() => {
    logger.info(`All done after 20 sec.`);
});

和许多其他人,但其中 none 按预期工作。在大多数情况下,消息 All done after 20 sec. 在 functionOne/functionTwo 启动后立即出现,或者根本不打印最终日志。

我要如何编写脚本?

也许你需要Promise.all()

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/all

The Promise.all() method takes an iterable of promises as an input, and returns a single Promise that resolves to an array of the results of the input promises. This returned promise will resolve when all of the input's promises have resolved, or if the input iterable contains no promises. It rejects immediately upon any of the input promises rejecting or non-promises throwing an error, and will reject with this first rejection message / error.

const promise1 = Promise.resolve(3);
const promise2 = 42;
const promise3 = new Promise((resolve, reject) => {
  setTimeout(resolve, 100, 'foo');
});

Promise.all([promise1, promise2, promise3]).then((values) => {
  console.log(values);
});
// expected output: Array [3, 42, "foo"]

在您的示例中,我注意到您不会解决 Promise:

而不是

 return new Promise(resolve => {
    setTimeout(function () {
      logger.info("Finished functionOne after 20 sec.");
    }, 20000);

尝试

 return new Promise(resolve => {
    setTimeout(function () {
       logger.info("Finished functionOne after 20 sec.");
       resolve(true); // or any value
    }, 20000);

首先,您必须同时修复 functionOne()functionTwo(),以便它们 resolve() 在计时器触发时创建的承诺。否则,他们只会创建一个永远不会解决的承诺,这不是很有用,并且在完成时不会通知调用者。

然后,并行地 运行 它们,使用 Promise.all() 这将允许您调用两个函数,然后它将一起跟踪返回的承诺以及从调用 [=15 返回的承诺=] 将在两个函数都完成时解析,或者如果其中一个函数拒绝了它们的承诺则拒绝。

如果你在这里的例子,none 的承诺被拒绝,但是如果你想知道所有的承诺何时完成,即使有些承诺被拒绝,那么你会使用 Promise.allSettled() 而不是 Promise.all().主要区别在于 Promise.all() 将短路并在您传递的任何承诺被拒绝时立即拒绝它的承诺,而 Promise.allSettled() 将等待所有承诺完成,而不管 resolve/reject。尽管您没有在此处使用它,但 Promise.allSettled() 的解析值也不同,因此您可以分辨哪些承诺被拒绝,哪些已解决。

这是一个使用 Promise.all() 的 运行nable(在代码片段中)示例。 如果这是您希望看到的行为,您可以换入 Promise.allSettled()

    const logger = console;

    const functionOne = function () {
      logger.info("Starting functionOne");
      return new Promise(resolve => {
        setTimeout(function () {
          logger.info("Finished functionOne after 20 sec.");
          resolve();
        }, 20000);
      });
    };
        
    const functionTwo = function () {
      logger.info("Starting functionTwo");
      return new Promise(resolve => {
        setTimeout(function () {
          logger.info("Finished functionTwo after 10 sec.");
          resolve();
        }, 10000);
      });
    };
    const runningFunctions = function () {
      logger.info('Start jobs');
      return Promise.all([functionOne(), functionTwo()]);
    }
    
    runningFunctions().then(() => {
        logger.info(`All done after 20 sec.`);
    }).catch(err => {
        console.log(err);
    });

请注意,如果您 运行 片段,您可以看到每个日志语句的实际时间,以验证事情是否在适当的时间发生。


到运行这两个函数依次调用,就可以await每个函数调用。这是一个 运行nable(在代码片段中)示例:

const logger = console;

const functionOne = function () {
  logger.info("Starting functionOne");
  return new Promise(resolve => {
    setTimeout(function () {
      logger.info("Finished functionOne after 20 sec.");
      resolve();
    }, 20000);
  });
};
    
const functionTwo = function () {
  logger.info("Starting functionTwo");
  return new Promise(resolve => {
    setTimeout(function () {
      logger.info("Finished functionTwo after 10 sec.");
      resolve();
    }, 10000);
  });
};
const runningFunctions = async function () {
  logger.info('Start jobs');
  await functionOne();
  await functionTwo();
}

runningFunctions().then(() => {
    logger.info(`All done after 30 sec.`);
}).catch(err => {
    console.log(err);
});

Promise.all 是要走的路,但你需要resolve 你的承诺

const logger = console;

const functionOne = function () {
  logger.info("Starting functionOne");
  return new Promise(resolve => {
    setTimeout(function () {
      logger.info("Finished functionOne after 20 sec.");
      resolve(); // need to resolve
    }, 20000);
  });
};
    
const functionTwo = function () {
  logger.info("Starting functionTwo");
  return new Promise(resolve => {
    setTimeout(function () {
      logger.info("Finished functionTwo after 10 sec.");
      resolve();
    }, 10000);
  });
};

Promise.all([functionOne(), functionTwo()]).then(() => {
    logger.info(`All done after 20 sec.`);
});

最好分享一个 jsfiddle / 工作代码以便更快地调试。这是您的参考的有效解决方案。

希望下面的JSfiddle能有所帮助。 https://jsfiddle.net/xqjvtypb/

const functionOne = function () {
  logger.info("Starting functionOne");
  return new Promise(resolve => {
    setTimeout(function () {
      logger.info("Finished functionOne after 20 sec.");
    }, 20000);
  });
};
    
const functionTwo = function () {
  logger.info("Starting functionTwo");
  return new Promise(resolve => {
    setTimeout(function () {
      logger.info("Finished functionTwo after 10 sec.");
    }, 10000);
  });
};

const runningFunctions = async function () {
  logger.info('Start jobs');
  Promise.all([functionOne(), functionTwo()]).then(() => {
    logger.info(`All done after 20 sec.`);
});
  /* functionOne() */;
  /* functionTwo() */;
}

runningFunctions().then(()=>{
// No action required.

});

您的主要功能应如下所示:

const runningFunctions = async function () {
  logger.info('Start jobs');
  await functionOne();
  await functionTwo();
  logger.info(`All done after 20 sec.`);
}

如果您不想等到另一个函数解决您需要使用 async/await

更多here

ES2020 中的新增功能是 Promise.allSettled

The Promise.allSettled() method returns a promise that resolves after all of the given promises have either fulfilled or rejected, with an array of objects that each describes the outcome of each promise.

这与 Promise.all 略有不同,后者在任何承诺被拒绝时立即拒绝。

function getData(count) {
  return new Promise((res, rej) => {
    setTimeout(() => {
      if (count !== 2) res(count);
      rej(count);
    }, 1000);
  });
}

async function main() {
  const promises = [getData(1), getData(2), getData(3)];
  const response = await Promise.allSettled(promises);
  console.log(response);
}

main();