JavaScript - 如何在 Promise 的 .then() 中调用异步函数

35

首先,我必须提到我已经查看了许多stackoverflow上的问题,但许多都没有回答我的问题。更不用说有许多根本就没有答案。

我该如何实现以下内容,确保functionA()执行完成后才执行functionB()?


注意:我不想将我的异步函数转换为new Promise(resolve=>{...})
因为我还必须将someServiceThatMakesHTTPCall()以及在调用堆栈中的任何其他异步函数进行转换,这是一个大的改变。

  function functionThatCannotHaveAsyncKeyword() {
      functionA()
        .then(async function() {
            await functionB();
        })
        .then(function() {
            console.log('last');
        });
  }

  async function functionA() {
      console.log('first');
      await someServiceThatMakesHTTPCall();
  }

  async function functionB() {
      console.log('second');
      await someServiceThatMakesHTTPCall();
  }

1
不,你不需要转换你的 someServiceThatMakesHTTPCall,因为异步函数和 Promises 没有区别。 - Christian Vincenzo Traina
async () => true // returns Promise - noetix
2
我建议你学习一下 Promise 的工作原理,因为 async/await 是它们的扩展(几乎是语法糖!)。我发誓这比你想象的要容易 :) - Christian Vincenzo Traina
如果那个函数不打算从其他地方 await,它可以是 async,因为它将返回一个被调用者 忽略Promise<void>。此外,在您的情况下,return functionB() 而不是使用 async-await 就足够了。 Translated text: 如果该函数不需要被其他东西 await,则可以使用 async,因为它将返回一个 Promise<void>,该承诺将被调用者 忽略。此外,在您的情况下,只需使用 return functionB() 而不是使用 async-await 即可。 - briosheje
@CristianTraìna,你有这方面的喜爱教程吗?我在取得一些进展后也应该发布我的教程。 - Nathan majicvr.com
3个回答

48

如果你只是想调用 async 函数并使其结果通过链条传播,那么在 async then 回调中使用 await 的方法会起作用,但这是不必要的复杂。但是如果你还有其他事情要做,并且想要利用 async 函数的语法优势,那也没关系。稍后我会回来讨论这个问题。

async 函数返回 promises,所以你只需要返回调用函数的结果:

function functionThatCannotHaveAsyncKeyword() {
    functionA()
        .then(function() {
            return functionB(someArgument);
        })
        .then(function() {
            console.log('last');
        }); // <=== Note: You need a `catch` here, or this function needs
            // to return the promise chain to its caller so its caller can
            // handle errors
}

如果你想把functionA的解析值直接传递给functionB,可以这样做:

functionA()
    .then(functionB)
    // ...

当你从then回调函数中返回一个promise时,then调用创建的promise会被解决为你返回的那个promise:它将等待另一个promise处于已定型状态,然后以相同的方式定型。
示例:

const wait = (duration, ...args) => new Promise(resolve => {
    setTimeout(resolve, duration, ...args);
});

async function functionA() {
    await wait(500);
    return 42;
}

async function functionB() {
    await wait(200);
    return "answer";
}

functionB()
.then(result => {
    console.log(result); // "answer"
    return functionA();
})
.then(result => {
    console.log(result); // 42
})
.catch(error => {
    // ...handle error...
});

回到你使用asyncthen回调函数的方法:这也是可行的,当你需要做更多操作时这种方法很有意义:

const wait = (duration, ...args) => new Promise(resolve => {
   setTimeout(resolve, duration, ...args);
});

async function functionA() {
    await wait(500);
    return 42;
}

async function functionB() {
    await wait(200);
    return "answer";
}

functionB()
.then(async (result) => {
    console.log(result); // "answer"
    const v = await functionA();
    if (v < 60) {
        console.log("Waiting 400ms...");
        await wait(400);
        console.log("Done waiting");
    }
    console.log(v);      // 42
})
.catch(error => {
    // ...handle error...
});


@ZhuHang - .then(async function() { return await functionB(); }) 运行良好,你的代码中可能存在其他问题。但是.then(function() { return functionB(); }) 更直接地实现了相同的功能。 - T.J. Crowder
@t-j-crowder 要将 Promise 链返回给其调用者,我必须使用 new Promise => resolve/reject 吗? - zhuhang.jasper
@T.J.Crowder,你是如何在5分钟内写出这么多代码的? - manish kumar
@ZhuHang - 不,只有在没有 Promise 时才需要 new Promise。当你已经有一个 Promise 时,只需返回该 Promise 即可。thencatchfinally 都会创建 Promise,因此通常返回调用它们的结果。更多信息请参见 - T.J. Crowder
1
@T.J.Crowder 你知道吗?抱歉,它实际上是可以工作的。又是其他问题。 - zhuhang.jasper
显示剩余15条评论

3
你可以在第一个方法中使用Promise,如下所示:
function functionThatCannotHaveAsyncKeyword() {
    return new Promise(async(resolve, reject)=> {
          await functionA();
          await functionB();
          console.log('last');
          resolve();    
      });
  }

  async function functionA() {
      console.log('first');
      await someServiceThatMakesHTTPCall();
  }

  async function functionB() {
      console.log('second');
      await someServiceThatMakesHTTPCall();
  }

2
我认为你需要在控制台日志后调用 resolve(),对吗? - zhuhang.jasper
@RahulPatil,您的解决方案几乎可行,但似乎需要按照zhuhang.jasper在评论中提到的方式进行更新。 - Serg

0
如果someServiceThatMakesHTTPCall是异步的,您可以通过以下方式避免所有这些问题:
function functionThatCannotHaveAsyncKeyword() {
    functionA()
        .then(function() {
            return functionB()
        })
        .then(function() {
            console.log('last');
        });
  }

  function functionA() {
      console.log('first');
      return someServiceThatMakesHTTPCall();
  }

  function functionB() {
      console.log('second');
      return someServiceThatMakesHTTPCall();
  }

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