【问题标题】:How to wait for all Promises to be finished如何等待所有 Promise 完成
【发布时间】:2021-11-25 02:07:02
【问题描述】:

我喜欢并行运行一组函数,当所有函数都完成后,我喜欢运行一些最终的东西。我的 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.`);
});

还有许多其他的,但没有一个按预期工作。在大多数情况下,消息 All done after 20 sec. 会在 functionOne/functionTwo 启动之后立即出现,或者根本不会打印最终日志。

如何编写脚本?

【问题讨论】:

  • Promise.all 是要走的路,但你需要在你的承诺中resolve
  • 而且你应该先解决你的承诺

标签: javascript node.js asynchronous promise


【解决方案1】:

ES2020 中的新功能是Promise.allSettled

Promise.allSettled() 方法返回一个在所有给定的 Promise 都已实现被拒绝后解析的 Promise,并带有一组对象,每个对象描述每个 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();

【讨论】:

    【解决方案2】:

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

    然后,要并行运行它们,请使用 Promise.all() 这将允许您调用这两个函数,然后它将一起跟踪两个返回的 Promise,并且从对 Promise.all() 的调用返回的 Promise 将在两个函数完成时解析或如果任一函数拒绝了他们的承诺,则拒绝。

    如果您的示例在这里,您的任何承诺都没有拒绝,但如果您想知道所有承诺何时完成,即使有些拒绝,那么您将使用Promise.allSettled() 而不是Promise.all()。主要区别在于Promise.all() 将在您传递的任何承诺被拒绝时立即短路并拒绝它的承诺,而Promise.allSettled() 将等到所有承诺都完成,而不管解决/拒绝如何。虽然您没有在这里使用它,但来自 Promise.allSettled() 的解析值也不同,因此您可以分辨哪些承诺被拒绝以及哪些被解决。

    这是一个使用Promise.all() 的可运行(在sn-p 中)示例。 如果这是您希望看到的行为,您可以换成 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);
        });

    注意,如果你运行sn -p,你可以看到每个日志语句的实际时间,以验证事情发生在适当的时间。


    要按顺序运行这两个函数,您只需await 每个函数调用即可。这是一个可运行的(在 sn-p 中)示例:

    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);
    });

    【讨论】:

      【解决方案3】:

      也许你需要Promise.all()

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

      Promise.all() 方法接受一个可迭代的 Promise 作为输入, 并返回一个解析为结果数组的 Promise 的输入承诺。这个返回的承诺将在所有 输入的承诺已经解决,或者如果输入可迭代包含 没有承诺。它立即拒绝任何输入承诺 拒绝或不承诺抛出错误,并会用这个拒绝 第一条拒绝消息/错误。

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

      【讨论】:

        【解决方案4】:

        在您的示例中,我注意到您不会解决 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);
        

        【讨论】:

          【解决方案5】:

          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.`);
          });
          

          【讨论】:

            【解决方案6】:

            最好分享一个 jsfiddle /working 代码以加快调试速度。这是您的参考的有效解决方案。

            希望下面的 JS fiddle 有所帮助。 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.
            
            });
            

            【讨论】:

            • 请保存对 cme​​ts 的评论并编辑问题以改进代码块/sn-ps,当它们可以改进时。请在可行的情况下使用 sn-ps 代替 JSFiddle 链接。
            【解决方案7】:

            你的主要功能应该是这样的:

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

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

            更多here

            【讨论】:

            • 我想并行启动函数。
            猜你喜欢
            • 1970-01-01
            • 2018-07-17
            • 1970-01-01
            • 1970-01-01
            • 2019-01-18
            • 2015-08-15
            • 2020-02-22
            • 2015-04-17
            • 1970-01-01
            相关资源
            最近更新 更多