Promises为我们提供了一种更简单的方式可以依次处理代码中的异步问题。考虑到我们的电脑不能有效地处理异步问题,因此这是一个非常受欢迎的补充。Async/await functions是ES2017 (ES8)新增的功能,进一步帮助我们在后台执行异步任务时编写完全同步的代码。

用异步函数实现的功能也可以通过结合promises和generators来实现,但是异步函数无需额外的公式化代码即可为我们提供需要的功能。

简单的例子

在下面的例子中,我们先声明一个函数,这个函数会返回一个在2秒之后解析值为🤡的promise对象。 接着我们声明一个async函数和一个await,在将消息输出到控制台之前等待promise调用resolve:

  1. function scaryClown() {
  2. return new Promise(resolve => {
  3. setTimeout(() => {
  4. resolve('🤡');
  5. }, 2000);
  6. });
  7. }
  8. async function msg() {
  9. const msg = await scaryClown();
  10. console.log('Message:', msg);
  11. }
  12. msg(); // Message: 🤡 <-- after 2 seconds

await是一个新的运算符,用于等待promise调用resolve或reject函数。它只能在异步函数中使用。

当涉及到多个步骤时,异步函数的强大之处变得更加明显:

  1. function who() {
  2. return new Promise(resolve => {
  3. setTimeout(() => {
  4. resolve('🤡');
  5. }, 200);
  6. });
  7. }
  8. function what() {
  9. return new Promise(resolve => {
  10. setTimeout(() => {
  11. resolve('lurks');
  12. }, 300);
  13. });
  14. }
  15. function where() {
  16. return new Promise(resolve => {
  17. setTimeout(() => {
  18. resolve('in the shadows');
  19. }, 500);
  20. });
  21. }
  22. async function msg() {
  23. const a = await who();
  24. const b = await what();
  25. const c = await where();
  26. console.log(`${ a } ${ b } ${ c }`);
  27. }
  28. msg(); // 🤡 lurks in the shadows <-- after 1 second

但是需要注意的是,在上面的示例中,每个步骤都是按顺序进行的,每个其他步骤都需要等前面的步骤先解决或拒绝,然后才能继续。相反的,如果你希望这些步骤并行进行,你可以简单地使用Promise.all等待所有promise都变为已成功的状态。

  1. // ...
  2. async function msg() {
  3. const [a, b, c] = await Promise.all([who(), what(), where()]);
  4. console.log(`${ a } ${ b } ${ c }`);
  5. }
  6. msg(); // 🤡 lurks in the shadows <-- after 500ms

一旦传入的所有promise的状态都变为已解决,Promise.all将返回一个包含已解决值的数组。在上面,我们还利用一些数组解构来使我们的代码更简洁。

Promise的返回

异步函数总是返回一个promise对象,因此以下内容可能不会产生你想要的结果:

  1. async function hello() {
  2. return 'Hello Alligator!';
  3. }
  4. const b = hello();
  5. console.log(b); // [object Promise] { ... }

由于返回的是promise对象,因此你可以执行下面的操作:

  1. async function hello() {
  2. return 'Hello Alligator!';
  3. }
  4. const b = hello();
  5. b.then(x => console.log(x)); // Hello Alligator!

…或者只是这样:

  1. async function hello() {
  2. return 'Hello Alligator!';
  3. }
  4. hello().then(x => console.log(x)); // Hello Alligator!

不同的声明方式

到目前为止,在我们的示例中,我们将异步函数视为函数声明,但是你也可以定义异步函数表达式和异步箭头函数:

异步函数表达式

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

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

异步箭头函数

再次是相同的示例,但这次将其定义为箭头函数:

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

错误处理

关于异步函数的另一点好处是,错误处理也可以使用旧的try … catch语句来完全同步进行。让我们通过一个有一半概率会返回拒绝状态的promise对象来演示:

  1. function yayOrNay() {
  2. return new Promise((resolve, reject) => {
  3. const val = Math.round(Math.random() * 1); // 0 or 1, at random
  4. val ? resolve('Lucky!!') : reject('Nope 😠');
  5. });
  6. }
  7. async function msg() {
  8. try {
  9. const msg = await yayOrNay();
  10. console.log(msg);
  11. } catch(err) {
  12. console.log(err);
  13. }
  14. }
  15. msg(); // Lucky!!
  16. msg(); // Lucky!!
  17. msg(); // Lucky!!
  18. msg(); // Nope 😠
  19. msg(); // Lucky!!
  20. msg(); // Nope 😠
  21. msg(); // Nope 😠
  22. msg(); // Nope 😠
  23. msg(); // Nope 😠
  24. msg(); // Lucky!!

鉴于异步函数总是返回一个promise对象,你也可以像平时使用catch语句一样处理未处理的错误:

  1. async function msg() {
  2. const msg = await yayOrNay();
  3. console.log(msg);
  4. }
  5. msg().catch(x => console.log(x));

同步的错误处理不仅在promise为拒绝状态时有效,在实际运行时或语法错误发生时也同样有效。在下面的例子中,
第二次调用我们的msg函数时,我们传入一个原型链中没有toUpperCase方法的数字类型的值。我们的try…catch块也可以捕捉到这个错误:

  1. function caserUpper(val) {
  2. return new Promise((resolve, reject) => {
  3. resolve(val.toUpperCase());
  4. });
  5. }
  6. async function msg(x) {
  7. try {
  8. const msg = await caserUpper(x);
  9. console.log(msg);
  10. } catch(err) {
  11. console.log('Ohh no:', err.message);
  12. }
  13. }
  14. msg('Hello'); // HELLO
  15. msg(34); // Ohh no: val.toUpperCase is not a function

异步函数和基于Promise的APIS

正如我们在Fetch API入门文章中所展示的那样,对于异步函数来说基于promise的web APIS也是一个理想的选择:

  1. async function fetchUsers(endpoint) {
  2. const res = await fetch(endpoint);
  3. let data = await res.json();
  4. data = data.map(user => user.username);
  5. console.log(data);
  6. }
  7. fetchUsers('https://jsonplaceholder.typicode.com/users');
  8. // ["Bret", "Antonette", "Samantha", "Karianne", "Kamren", "Leopoldo_Corkery", "Elwyn.Skiles", "Maxime_Nienow", "Delphine", "Moriah.Stanton"]

浏览器支持:
QQ截图20191105100657.png

原文地址:
https://alligator.io/js/async-functions/