【问题标题】:Run a callback after multiple function have completed多个函数完成后运行回调
【发布时间】:2018-11-05 01:44:39
【问题描述】:

我有多个耗时的函数,我想在它们都完成后运行一个函数,例如:

data.x = thisTakes2Seconds();
data.y = thisTakes5Seconds();
http.post(data);

我对 Javascript 中回调的概念很熟悉,但是如果我有几个函数,我真的应该嵌套回调几个函数吗?

【问题讨论】:

  • 只有在每个函数都需要完成前一个函数才能执行时,才应该嵌套回调。
  • @NathanHinchey 但在这种情况下,我的http.post 需要完成所有以前的功能(或data 将不完整)
  • 开始使用 Promise。那么使用Promise.all 真的很简单。
  • 上面的代码看起来是同步的(thisTakesXSeconds 函数返回它们的结果)。无需回调,代码只需约 7 秒即可运行。

标签: javascript callback


【解决方案1】:

为了方便处理异步函数,最好的方法是使用promisesasync/await

function thisTakes2Seconds() {
  return new Promise(resolve => setTimeout(() => resolve(3), 200)); // 0.2 to avoid waiting :P
}

function thisTakes5Seconds() {
  return new Promise(resolve => setTimeout(() => resolve(5), 500));
}

async function foo() {
  const data = {};
  
  data.x = await thisTakes2Seconds();
  data.y = await thisTakes5Seconds();
  
  // This will run once both promises have been resolved
  console.log(data);
}

foo()
  .then(() => console.log('done!')
  .catch(err => console.error(err));

如果您希望同时执行这两个功能,您可以这样做,然后使用 Promise.all 等待两者完成

async function foo() {
  const data = {};

  // Promise.all returns an array where each item is the resolved
  // value of the promises passed to it, maintaining the order
  // So we use destructuring to assign those values
  [data.x, data.y] = await Promise.all([
    thisTakes2Seconds(),
    thisTakes5Seconds()
  ]);

  console.log(data);
}

如果您已经有一个使用回调的异步函数,您可以轻松地将其转换为 Promise。

function myAsyncFunction(callback) {
    setTimeout(() => {
        callback(Math.random());
    }, 200);
}

function myAsyncFunctionPromise() {
     return new Promise((resolve, reject) => {
         myAsyncFunction(resolve);
         // If there is an error callback, just pass reject too.
     });
}

像 bluebird 这样的库已经有一个实用方法来承诺回调 API。

http://bluebirdjs.com/docs/api/promise.promisify.html


如果你在浏览器上运行它,并且需要支持过时的,你可以使用 babel 将 async/await 转换为 ES5

【讨论】:

    【解决方案2】:

    您的thisTakesXSeconds 函数会立即返回它们的结果。这告诉我们它们是同步的。无需回调,该代码只需约 7 秒即可运行。


    如果 thisTakesXSeconds 启动了一个耗时 X 秒的异步 进程(尽管您返回结果表明并非如此),我们将研究管理完成过程的方法。

    我真的应该嵌套回调几个函数吗?

    这个问题,以及对答案“是”的普遍不满,是我们现在拥有承诺甚至async 功能的原因。 :-)

    你可以让你的thisTakesXSeconds 函数返回一个promise,然后如果函数可以并行运行,就按照这些思路做一些事情:

    Promise.all([
        thisTakes2Seconds(),
        thisTakes5Seconds()
    ])
    .then(([x, y]) => {
        data.x = x;
        data.y = y;
        // use or return `data` here
    })
    // return the promise or add a `catch` handler
    

    如果他们需要串联运行(一个接一个),那么

    thisTakes2Seconds()
        .then(x => {
            data.x = x;
            return thisTakes5Seconds();
        })
        .then(y => {
            data.y = y;
            // use or return `data` here
        })
        // return the promise or add a `catch` handler
    

    ...在async 函数中看起来更清晰:

    data.x = await thisTakes2Seconds();
    data.y = await thisTakes5Seconds();
    // use or return `data` here
    // add appropriate error handling (at this level or when calling the function)
    

    【讨论】:

      【解决方案3】:

      在执行多个异步调用后,我用来处理执行某些代码的一种技术是使用“已完成”计数器或对象。

      每个函数都会执行一个回调,其中包含

      if (counter == numberOfFuntionsIWantedToComplete) 
          doTheAfterWeHaveAllDataThing`
      

      【讨论】:

        猜你喜欢
        • 2017-11-10
        • 2019-03-25
        • 1970-01-01
        • 2018-10-30
        • 1970-01-01
        • 1970-01-01
        • 2023-02-04
        • 1970-01-01
        • 2014-09-28
        相关资源
        最近更新 更多