探索JavaScript中的Async/Await函数

来自菜鸟教程
跳转至:导航、​搜索

介绍

Promises 为我们提供了一种更简单的方法来以顺序方式处理代码中的异步。 考虑到我们的大脑并不是为有效处理异步性而设计的,这是一个非常受欢迎的补充。 Async/await 函数 是 ES2017 (ES8) 的新增功能,它帮助我们在编写完全同步的代码的同时在后台执行异步任务。

使用异步函数实现的功能可以通过将 Promise 与 生成器 组合来重新创建,但异步函数无需任何额外的样板代码即可为我们提供所需的功能。

简单示例

在下面的示例中,我们首先声明一个函数,该函数返回一个承诺,该承诺会在 2 秒后解析为 🤡 的值。 然后,我们声明一个 async 函数和 await 以在将消息记录到控制台之前解析承诺:

function scaryClown() {
  return new Promise(resolve => {
    setTimeout(() => {
      resolve('🤡');
    }, 2000);
  });
}

async function msg() {
  const msg = await scaryClown();
  console.log('Message:', msg);
}

msg(); // Message: 🤡 <-- after 2 seconds

await 是一个新的操作符,用于等待一个 promise 解决或拒绝。 它只能在异步函数中使用。


当涉及多个步骤时,异步函数的力量变得更加明显:

function who() {
  return new Promise(resolve => {
    setTimeout(() => {
      resolve('🤡');
    }, 200);
  });
}

function what() {
  return new Promise(resolve => {
    setTimeout(() => {
      resolve('lurks');
    }, 300);
  });
}

function where() {
  return new Promise(resolve => {
    setTimeout(() => {
      resolve('in the shadows');
    }, 500);
  });
}

async function msg() {
  const a = await who();
  const b = await what();
  const c = await where();

  console.log(`${ a } ${ b } ${ c }`);
}

msg(); // 🤡 lurks in the shadows <-- after 1 second

但是需要注意的是,在上面的示例中,每个步骤都是按顺序完成的,每个附加步骤都等待前一个步骤解决或拒绝,然后再继续。 如果您希望这些步骤并行发生,您可以简单地使用 Promise.all 来等待所有承诺都已实现:

// ...

async function msg() {
  const [a, b, c] = await Promise.all([who(), what(), where()]);

  console.log(`${ a } ${ b } ${ c }`);
}

msg(); // 🤡 lurks in the shadows <-- after 500ms

Promise.all 在所有传入的 Promise 都解析后返回一个包含解析值的数组。

在上面我们还使用了一些不错的 数组解构 来使我们的代码简洁。

承诺回报

异步函数总是返回一个承诺,所以以下可能不会产生你想要的结果:

async function hello() {
  return 'Hello Alligator!';
}

const b = hello();

console.log(b); // [object Promise] { ... }

由于返回的是一个承诺,你可以这样做:

async function hello() {
  return 'Hello Alligator!';
}

const b = hello();

b.then(x => console.log(x)); // Hello Alligator!

…或者只是这个:

async function hello() {
  return 'Hello Alligator!';
}

hello().then(x => console.log(x)); // Hello Alligator!

不同的形式

So far with our examples we saw the async function as a function declaration, but we can also define async function expressions and async arrow functions:

异步函数表达式

这是我们第一个示例中的异步函数,但定义为函数表达式:

const msg = async function() {
  const msg = await scaryClown();
  console.log('Message:', msg);
}

异步箭头函数

这是同样的例子,但这次定义为箭头函数:

const msg = async () => {
  const msg = await scaryClown();
  console.log('Message:', msg);
}

错误处理

异步函数的另一个优点是错误处理也是完全同步完成的,使用良好的旧 try...catch 语句。 让我们使用一个会拒绝一半时间的 Promise 来演示:

function yayOrNay() {
  return new Promise((resolve, reject) => {
    const val = Math.round(Math.random() * 1); // 0 or 1, at random

    val ? resolve('Lucky!!') : reject('Nope 😠');
  });
}

async function msg() {
  try {
    const msg = await yayOrNay();
    console.log(msg);
  } catch(err) {
    console.log(err);
  }
}

msg(); // Lucky!!
msg(); // Lucky!!
msg(); // Lucky!!
msg(); // Nope 😠
msg(); // Lucky!!
msg(); // Nope 😠
msg(); // Nope 😠
msg(); // Nope 😠
msg(); // Nope 😠
msg(); // Lucky!!

鉴于异步函数总是返回一个 Promise,您还可以像通常使用 catch 语句一样处理未处理的错误:

async function msg() {
  const msg = await yayOrNay();
  console.log(msg);
}

msg().catch(x => console.log(x));

这种同步错误处理不仅在 Promise 被拒绝时有效,而且在发生实际运行时或语法错误时也有效。 在下面的示例中,第二次调用 msg 函数时,我们传入一个 number 值,该值在其原型链中没有 toUpperCase 方法。 我们的 try...catch 块也捕获了该错误:

function caserUpper(val) {
  return new Promise((resolve, reject) => {
    resolve(val.toUpperCase());
  });
}

async function msg(x) {
  try {
    const msg = await caserUpper(x);
    console.log(msg);
  } catch(err) {
    console.log('Ohh no:', err.message);
  }
}

msg('Hello'); // HELLO
msg(34); // Ohh no: val.toUpperCase is not a function

具有基于 Promise 的 APIS 的异步函数

正如我们在 Fetch API 入门中所展示的,基于 Promise 的 Web API 是异步函数的完美候选者:

async function fetchUsers(endpoint) {
  const res = await fetch(endpoint);
  let data = await res.json();

  data = data.map(user => user.username);

  console.log(data);
}

fetchUsers('https://jsonplaceholder.typicode.com/users');
// ["Bret", "Antonette", "Samantha", "Karianne", "Kamren", "Leopoldo_Corkery", "Elwyn.Skiles", "Maxime_Nienow", "Delphine", "Moriah.Stanton"]

浏览器支持:截至 2020 年,94% of 全球浏览器可以处理 javascript 中的 async/await IE11 和 Opera Mini 除外。


结论

Async/await 函数之前, JavaScript 代码依赖于大量异步事件 (例如:对 API 进行大量调用的代码) 最终会陷入所谓的“回调地狱” ” - 一系列很难阅读和理解的函数和回调。

Async 和 await 允许我们编写读取更清晰的异步 JavaScript 代码。