在多个函数完成后运行回调函数

4

我有多个耗时的函数,我希望在它们全部完成后运行一个函数,例如:

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

我了解Javascript中回调函数的概念,但如果我有多个函数,我真的需要嵌套多层回调函数吗?


只有在每个函数需要前一个函数完成才能执行时,才应该嵌套回调函数。 - Nathan Hinchey
@NathanHinchey 但在这种情况下,我的 http.post 需要所有先前的函数完成(否则 data 将不完整)。 - Juicy
1
开始使用 Promise。然后使用 Promise.all 真的非常简单。 - Bergi
上面的代码看起来是同步的(thisTakesXSeconds函数返回它们的结果)。不需要回调,代码将花费约7秒钟运行。 - T.J. Crowder
3个回答

2
处理异步函数的最佳方法是使用 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);
}

如果您已经有一个使用回调函数的异步函数,您可以轻松地将其转换为 promises。
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转换为Promise。

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


如果您在浏览器中运行它并需要支持过时的浏览器,您可以使用Babel将async/await转译为ES5。

1
你的 thisTakesXSeconds 函数立即返回它们的结果。这告诉我们它们是同步的。不需要回调函数,该代码将花费约 7 秒钟运行。
如果thisTakesXSeconds启动了一个异步进程,需要 X 秒钟才能完成(虽然你“返回”结果的事实似乎表明不是这样),我们将寻找管理完成过程的方法。
“我真的应该嵌套几个回调函数吗?”这个问题以及对“是”的一般不满意,这就是为什么我们现在有了 Promise 甚至是 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)

0

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

每个函数都执行包含回调的操作。

if (counter == numberOfFuntionsIWantedToComplete) 
    doTheAfterWeHaveAllDataThing`

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接