第九章 JavaScript 异步编程专题 9.4 async/await

71 阅读3分钟

9.4 async/await

虽然使用 Promise 可以解决回调地狱的问题,但是依然需要大量的 then 和 catch 方法,代码仍然相对复杂。 在 ECMAScript 2017 (也称作 ES8)中引入了 async/await,它是一种更加简洁、直观的异步编程方式。通过使用 Async/Await,我们可以将异步代码看作是同步代码来编写和理解,从而使得代码更加易读、易维护。 async 关键字用于标识一个函数是异步函数,而 await 关键字则用于等待异步操作完成并返回结果。 使用 Async/Await 编写的异步代码,基本上可以分为以下几步: 1 创建一个异步函数,使用 async 关键字标识。异步函数可以包含多个异步操作,每个异步操作需要使用 await 关键字等待执行结果。 2 在异步函数中使用 await 关键字等待异步操作的结果。等待一个异步操作时,异步函数会暂停执行,直到该异步操作返回结果。 3 如果异步操作返回的结果是一个 Promise 对象,则可以使用 then 方法继续处理结果;如果异步操作出现错误,则可以使用 catch 方法捕获错误并进行处理。 下面是一个使用 async/await 编写的示例:

async function fetchData() {
  try {
    const response = await fetch('https://api.example.com/data');
    const data = await response.json();
    console.log(data);
  } catch (error) {
    console.error(error);
  }
}

在上面的示例中,fetchData 函数使用了 async 关键字来标识自己是一个异步函数。在函数内部,使用了 await 关键字来等待异步操作完成,fetch 函数返回一个 Promise 对象,await 将会等待该 Promise 对象完成并返回一个响应对象。接着,使用 response.json() 方法将响应对象转换为 JSON 格式的数据,并将结果赋值给 data 变量。最后,使用 console.log 输出获取到的数据。 下面是使用 async/await 重写回调地狱的例子:

async function loadData(url) {
  return new Promise(function(resolve, reject) {
    var xhr = new XMLHttpRequest();
    xhr.open('GET', url, true);
    xhr.onreadystatechange = function() {
      if (xhr.readyState === 4) {
        if (xhr.status === 200) {
          resolve(xhr.responseText);
        } else {
          reject('Error loading data: ' + xhr.status);
        }
      }
    };
    xhr.send();
  });
}

async function loadUser(userId) {
  try {
    const userData = await loadData('/user/' + userId);
    return JSON.parse(userData);
  } catch (error) {
    throw new Error('Error loading user: ' + error);
  }
}

async function loadOrders(userId) {
  try {
    const orderData = await loadData('/orders/' + userId);
    return JSON.parse(orderData);
  } catch (error) {
    throw new Error('Error loading orders: ' + error);
  }
}

async function loadDetails(orderId) {
  try {
    const detailsData = await loadData('/details/' + orderId);
    return JSON.parse(detailsData);
  } catch (error) {
    throw new Error('Error loading details: ' + error);
  }
}

async function loadDataFlow() {
  try {
    const user = await loadUser('123');
    console.log('Loaded user:', user);
    const orders = await loadOrders(user.id);
    console.log('Loaded orders:', orders);
    const details = await loadDetails(orders[0].id);
    console.log('Loaded details:', details);
  } catch (error) {
    console.error(error);
  }
}

loadDataFlow();

在上面的示例中,我们将 loadUser、loadOrders 和 loadDetails 函数都转换为了异步函数,使用了 async 关键字来标识。同时,我们使用 await 关键字等待异步操作完成并返回结果。 注意到我们在每个异步函数中使用了 try...catch 语句来捕获异步操作中的错误,并将错误通过 throw new Error 抛出,以便后续处理。 在最后的 loadDataFlow 函数中,我们使用了 await 关键字等待异步操作完成,并使用 console.log 输出获取到的数据。同时,我们也使用了 try...catch 语句来处理异步操作中的错误,通过 console.error 输出错误信息。 需要注意的是,使用 async/await 编写异步代码时,需要注意以下几点: 1.async/await 在代码结构上比 Promise 更加简洁易懂,但是需要注意异常处理和错误传递。 2.在使用 await 关键字时,需要将其放置在一个 async 函数内部。如果在普通函数中使用 await 关键字,将会出现语法错误。 async/await 可以看作是 Promise 的语法糖,它是在 Promise 基础之上提供了更加简洁易懂的语法来处理异步操作。实际上,使用 async/await 编写异步代码时,内部仍然使用了 Promise 对象,只是将其包装在 async/await 语法之下,可以避免使用链式调用的方式来处理异步操作,使得代码更加简洁易懂,同时也能够更加方便地处理异步操作中的错误。