ES8 的优美语法 Async / Await:Promise 的进化版?

ES8 是 ECMAScript 的第八个版本,也是最新的一个版本。在 ES8 中,加入了 Async / Await 这种语法糖,使得异步编程更加简单、易懂。本文将介绍 Async / Await 的使用方法、优美语法背后的实现原理,以及与 Promise 的异同之处。

1. Async / Await 的使用方法

Async / Await 是基于 Promise 实现的语法糖,用于解决 Promise 中回调嵌套过多的问题。它在语法上使用起来更加简洁易懂,代码可读性更强。下面是一个简单的例子:

function delay(ms) {
  return new Promise(resolve => setTimeout(resolve, ms));
}

async function asyncDelay() {
  console.log('before delay');
  await delay(1000); // 等待 1 秒钟
  console.log('after delay');
}

asyncDelay();

以上的代码中,我们定义了一个 delay 函数,它返回一个 Promise,并在一定时间后 resolve。我们接着定义了一个 async 函数 asyncDelay,其中使用了 await 关键字来等待 delay 函数的结果。最后的 asyncDelay() 调用会输出以下结果:

可以看出,asyncDelay 函数执行时会首先输出 'before delay',然后等待 1 秒钟,最后输出 'after delay'。

在实际应用中,Async / Await 很容易与其他操作一起使用,如网络请求、数据库操作等。接下来,我们将详细介绍 Async / Await 的实现原理。

2. Async / Await 的实现原理

在前面的例子中,我们已经使用了 await 关键字来等待 Promise 对象的结果。那么,await 关键字是如何实现等待的呢?

在普通函数或类中使用 await 关键字会报错,但在 async 函数(也称异步函数)中使用它可以起到类似暂停的效果,使得代码在等待异步操作完成后再行执行。在实际执行时,async 函数的返回值是一个 Promise 对象。

换句话说,Async / Await 执行的本质还是 Promise。当我们需要等待一些异步操作完成时,可以使用 await,但必须在 async 函数中使用。async 函数将异步操作封装成一个 Promise 对象,同时还可以像普通函数一样传递参数。这样一来,我们就不必再使用 Promise 的回调嵌套,大大简化了异步编程的复杂度。

下面我们来看一个实现 Async / Await 的伪代码:

function asyncToGenerator(generatorFunc) {
  // 返回一个新函数
  return function () {
    // 获取迭代器对象
    const gen = generatorFunc.apply(this, arguments);
    // 定义 next 函数
    function next(data) {
      // 获取下一个 yield 的结果
      const { value, done } = gen.next(data);
      if (done) {
        // 如果迭代器已结束,则返回 Promise
        return Promise.resolve(value);
      } else {
        // 否则继续迭代
        return Promise.resolve(value).then(res => next(res), err => gen.throw(err));
      }
    }
    return next();
  }
}

以上的代码中,我们定义了一个 asyncToGenerator 函数,它接受一个 generator 函数作为参数,并返回一个新函数。在该新函数中,我们获取到了 generator 函数的迭代器对象 gen,然后定义了 next 函数。

在 next 函数中,我们首先获取下一个 yield 的结果。如果迭代器已经结束,则返回一个 Promise 对象并解析出结果值。如果迭代器还未结束,则使用 Promise.resolve 将该值转换成一个 Promise 对象,并将其作为参数传递给 next 函数继续进行迭代。如果出现异常,则使用 gen.throw 抛出。

以上是 Async / Await 的实现原理的简单解释,接下来我们将和 Promise 做一个对比,看一下二者在使用上的异同之处。

3. Async / Await 与 Promise 的异同

异步编程在 JavaScript 中是一项很重要的功能。在 ES6 之前,Promise 是比较好的异步解决方案,但在实际使用过程中,仍然存在一些回调嵌套及复杂性的问题。因此,在 ES8 中,Async / Await 语法糖被引入,以简化异步编程。

与 Promise 相比,Async / Await 的语法更加优雅,使得异步代码更加易读、易写。以下是 Async / Await 和 Promise 的对比:

3.1 使用方式

Promise 通过两个方法 then() 和 catch() 来处理异步操作:

fetch('https://api.github.com/users')
  .then(res => res.json())
  .then(users => console.log(users))
  .catch(err => console.log(err));

而 Async / Await 则通过 async 和 await 关键字来改变异步编码的方式:

async function getUsers() {
  try {
    const res = await fetch('https://api.github.com/users');
    const users = await res.json();
    console.log(users);
  } catch (err) {
    console.log(err);
  }
}

getUsers();

可以看出,Async / Await 更加符合人的思维方式,代码也更加优雅。

3.2 错误处理

在 Promise 中,我们可以使用 catch() 方法来处理异常:

fetch('https://api.github.com/users')
  .then(res => res.json())
  .catch(err => console.log(err));

而在 Async / Await 中,我们可以使用 try..catch 捕获异常:

async function getUsers() {
  try {
    const res = await fetch('https://api.github.com/users');
    const users = await res.json();
    console.log(users);
  } catch (err) {
    console.log(err);
  }
}

getUsers();

3.3 并发请求

在 Promise 中,我们可以使用 Promise.all() 方法并发执行多个异步操作,并在所有异步操作完成后返回一个数组:

Promise.all([
  fetch('https://api.github.com/users/1'),
  fetch('https://api.github.com/users/2'),
  fetch('https://api.github.com/users/3')
]).then(responses => {
  const results = responses.map(res => res.json());
  console.log(results);
});

而在 Async / Await 中,我们可以使用 Promise.all() 方法并发执行多个异步操作,并在所有异步操作完成后返回一个数组:

async function getUsers() {
  const response1 = fetch('https://api.github.com/users/1');
  const response2 = fetch('https://api.github.com/users/2');
  const response3 = fetch('https://api.github.com/users/3');
  const [user1, user2, user3] = await Promise.all([response1, response2, response3]);
  console.log(user1, user2, user3);
}

getUsers();

以上就是 Async / Await 和 Promise 的使用方法及异同之处的详细介绍。

4. 总结

Async / Await 是在 ES8 中引入的一种优美的异步编程方式,它基于 Promise 实现,使得异步操作更加易读、易写。在使用 Async / Await 时,我们需要将异步操作封装成一个 Promise 对象,并在 async 函数中使用 await 关键字等待其结果。

与 Promise 相比,Async / Await 的代码更加优雅、易读,使得异步编程更加简单。同时,Async / Await 可以通过 try..catch 捕获异常、使用 Promise.all() 并发执行多个异步操作,更加灵活实用。

虽然 Async / Await 给了我们更多的便捷,但在实际开发过程中,我们仍需要将它和 Promise 结合使用,以避免一些因异步编程带来的问题。

来源:JavaScript中文网 ,转载请注明来源 本文地址:https://www.javascriptcn.com/post/65a48607add4f0e0ffccede3


纠错反馈