理解NodeJS上的async/await

22

我认为我的理解可能受到我对.NETasync/await经验的影响,因此我想要一些代码示例:

我正在尝试让一个Express控制器等待5秒钟才返回响应:

const getUsers = async (ms) => {
  var wait = ms => new Promise(resolve => setTimeout(resolve, ms));

  await wait(ms);
};


export const index = (req, res) => {
  async () => {
    await getUsers(5000);

    res.json([
      {
        id: 1,
        name: 'John Doe',
      },
      { id: 2,
        name: 'Jane Doe',
      },
    ]);
  };
};

这段代码不起作用,浏览器一直在加载,但是什么都没有显示。

getUser函数是我基于这个SO答案建立的,控制器方法是基于我的(错误)理解工作的,所以我想要一些澄清和更正:

1. 我应该在何时使用await

据我理解,在async函数调用之前应该使用await。这正确吗?此外,为什么可以在返回Promise的非异步函数之前调用await?

2. 何时应该使用async

据我理解,将一个函数标记为async,以便可以使用await关键字调用它。这正确吗?此外,为什么必须将await getUsers(5000)调用包装在匿名async函数中?


1
不要将东西包装在异步匿名函数中,而这个函数在你的代码中也从未被执行。相反,使索引函数成为异步函数,并删除函数包装和返回res.json。 - Joel Harkes
4个回答

23

澄清一些疑虑 -

  1. 您可以使用await与任何返回 Promise 的函数一起使用。您等待的函数不一定需要是async
  2. 当您想在函数内使用await关键字时,应使用async函数。如果您不会在函数内使用await关键字,则不需要将该函数设置为async
  3. async函数默认返回一个 Promise。这就是您能够等待async函数的原因。

来自MDN -

调用 async 函数时,它会返回一个 Promise。

至于您的代码,可以写成这样 -

const getUsers = (ms) => { // No need to make this async
    return new Promise(resolve => setTimeout(resolve, ms));
};

// this function is async as we need to use await inside it
export const index = async (req, res) => {
    await getUsers(5000);

    res.json([
      {
        id: 1,
        name: 'John Doe',
      },
      { id: 2,
        name: 'Jane Doe',
      },
    ]);
};

1
这个解释非常清晰。现在,剩下的就是将导出的异步函数index(它返回一个Promise)与Express路由器连接起来,使用一个接受Promise的router.get变体。这样正确吗? - Christopher Francisco
1
在Express中,您可以像这样使用它 - router.get('/users', index) - Jyotman Singh

14

Javascript中的异步函数

async关键字将普通的JS函数声明转换为异步函数声明:

function syncFunc {// dostuff}
async function asyncFunc {// dostuff} // the async keyword is placed before the function keyword

异步函数会返回一个 Promise:

  • 当异步函数返回一个值时,Promise 将会被解析为返回值。
  • 当异步函数抛出异常或某个值时,Promise 将会被拒绝并带有抛出的值。

在异步函数内部,你可以使用 await 关键字。在 promise 前放置 await 会使异步函数暂停直到 promise 被解决(拒绝或实现)。

  • 当 promise 实现时,await 表达式的值是已实现 promise 的值。
  • 当 promise 被拒绝时,await 表达式抛出被拒绝的值。

示例:

function makePromise(x) { 
    return new Promise(resolve => {
      setTimeout(() => {
        resolve(x);
      }, 1000);
    });
  }
  
  async function asyncFunc() {
    var x = await makePromise(1); // the function is paused here until the promise is fulfilled
    console.log(x); // logs 1
    return x;
  }
  
  const returnedProm = asyncFunc(); // the async func returns a promise


  returnedProm.then((x) => console.log(x));
  // This promise is fulfilled with the return value from the async func, so this logs 1

何时使用异步函数:

当您有多个相互依赖的异步操作(以承诺形式实现)时,异步函数是一个非常有用的工具。例如,当第二个承诺需要第一个承诺提供的数据时。现在,您可以方便地使用 await 关键字来首先接收承诺 1 中的数据,并将此数据作为参数传递给承诺 2。

换句话说,异步函数可以通过 await 关键字使异步编程行为看起来像同步编程。因此,应用程序更容易理解。


说得好:换句话说,异步函数可以通过await关键字使异步编程表现得像同步编程。因此,您的应用程序更容易理解。 - hatef

4
你可以在async函数中await任何承诺。在你等待的承诺完成后,await后面的代码将被执行。这是传统JavaScript回调的一个很好的替代方法。我写了一篇关于它的博客-> https://github.com/Gameye/js-async希望这能帮到你!

3

async await只是Promise的语法糖。

当你想让代码在函数完成后运行时,使用它们就像使用Promise一样。

async function asyncOperation(callback) {
    const response = await asyncStep1();
    return await asyncStep2(response);
}

如果您使用Promises模式的语法,则“is the exact thing”是确切的事情。
function asyncOperation() {
  return asyncStep1(...)
    .then(asyncStep2(...));
}

新的async/await语法允许你仍然使用Promise,但它消除了需要提供回调给链接的then()方法的需求。
回调直接从异步函数中返回,就像它是一个同步阻塞函数一样。
let value = await myPromisifiedFunction(); 

当您计划在函数中使用await时,应使用async关键字标记函数(如c#中所示)。
您不必将getUsers创建为匿名函数。

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