Async和Await

同步和异步的对比

同步代码

每一行代码必须等到前一行代码执行完毕后才能继续执行。

console.log('Step 1');
console.log('Step 2');
console.log('Step 3');

 以上代码将会依次打印:

Step 1
Step 2
Step 3
异步代码

某些操作可以在后台运行,同时继续执行其他代码。 

console.log('Step 1');

setTimeout(() => {
  console.log('Step 2');
}, 1000);

console.log('Step 3');

以上代码将会先打印 Step 1Step 3,然后在1秒后打印 Step 2

Step 1
Step 3
Step 2
使用 Promise 的异步操作

假设我们有一个异步函数 fetchData,它模拟从服务器获取数据,返回一个 Promise

function fetchData(url) {
  return new Promise((resolve) => {
    setTimeout(() => {
      resolve(`Data from ${url}`);
    }, 2000); // 模拟2秒的网络延迟
  });
}

fetchData('https://api.example.com/data')
  .then((data) => {
    console.log(data);
  });

console.log('Fetching data...');

 这个代码将先打印 Fetching data...,然后2秒后打印 Data from https://api.example.com/data

Fetching data...
Data from https://api.example.com/data
使用 asyncawait 的异步操作

通过 asyncawait,我们可以更直观地表达异步操作:

 

async function getData() {
  console.log('Fetching data...');

  const data = await fetchData('https://api.example.com/data');
  console.log(data);
}

getData();

上面的代码看起来更像同步代码,但实际上是异步的。这里发生了什么?

  1. getData 函数是一个异步函数(用 async 关键字声明)。
  2. 当执行到 await fetchData('https://api.example.com/data') 时,getData 函数会暂停,等待 fetchData 返回的 Promise 完成。
  3. Promise 完成(即 fetchData 返回的数据被解析)之前,getData 函数内部的其他代码不会执行。
  4. Promise 完成时,await 表达式会返回 Promise 的解析值,然后继续执行 getData 函数后面的代码。

形象化示例

想象你在看一部电影。你把电影暂停,去拿了一袋爆米花,然后再回来继续看电影。暂停和继续的过程是这样的:

  1. 暂停电影await fetchData('https://api.example.com/data') 暂停函数执行。
  2. 去拿爆米花:函数等待 Promise 完成。与此同时,你可以做其他事情(比如处理其他异步操作)。
  3. 返回继续看电影Promise 完成,await 返回结果,继续执行函数后面的代码。
为什么使用暂停

使用 await 暂停当前的异步函数有以下几个好处:

  1. 代码更易读:异步代码看起来更像同步代码,减少了嵌套和回调地狱。
  2. 避免错误:通过等待 Promise 完成,可以确保在使用异步操作结果时,这些结果是可用的。
  3. 更好的错误处理:使用 try...catch 可以更方便地捕获异步操作中的错误。

 

JavaScript 中的异步处理

使用 Promise 处理异步

传统上,JavaScript 使用 Promise 来处理异步操作:

function cookRice() {
  return new Promise((resolve) => {
    setTimeout(() => {
      resolve('Rice is ready');
    }, 3000); // 假设煮饭需要3秒
  });
}

function cookDish() {
  return new Promise((resolve) => {
    setTimeout(() => {
      resolve('Dish is ready');
    }, 2000); // 假设炒菜需要2秒
  });
}

function cookSoup() {
  return new Promise((resolve) => {
    setTimeout(() => {
      resolve('Soup is ready');
    }, 4000); // 假设煲汤需要4秒
  });
}

cookRice().then((message) => {
  console.log(message);
  return cookDish();
}).then((message) => {
  console.log(message);
  return cookSoup();
}).then((message) => {
  console.log(message);
});

在上面的代码中,我们使用了 Promisethen 方法来处理异步操作。虽然这解决了问题,但代码看起来有点复杂,嵌套层次较深。

使用 asyncawait 处理异步

asyncawait 提供了一种更清晰、更直观的方式来处理异步操作。我们可以这样重写上面的例子:

async function cookMeal() {
  try {
    const rice = await cookRice();
    console.log(rice);

    const dish = await cookDish();
    console.log(dish);

    const soup = await cookSoup();
    console.log(soup);
  } catch (error) {
    console.error('Error:', error);
  }
}

cookMeal();

解释

  1. async 函数

    • async 关键字用于声明一个异步函数。这个函数会自动返回一个 Promise,即使你没有显式返回一个 Promise
  2. await 表达式

    • await 关键字只能在 async 函数内部使用。
    • await 会暂停 async 函数的执行,等待 Promise 完成,并返回 Promise 的结果。
    • await 期间,JavaScript 事件循环仍然会处理其他任务(比如用户输入、网络请求等),不会阻塞整个程序

可以具体地探讨一下这个过程,并通过一个完整的示例来说明 asyncawait 是如何工作的。

假设我们在 getData 函数后面添加一个打印语句:

function fetchData(url) {
  return new Promise((resolve) => {
    setTimeout(() => {
      resolve(`Data from ${url}`);
    }, 2000); // 模拟2秒的网络延迟
  });
}

async function getData() {
  console.log('Fetching data...');

  const data = await fetchData('https://api.example.com/data');
  console.log(data);
}

getData();

console.log('This message is printed immediately after calling getData');

 

执行顺序解析

  1. 调用 getData 函数

    • 首先执行 getData(),这个调用启动了 getData 函数。
    • getData 函数开始执行,并打印 Fetching data...
  2. 遇到 await 表达式

    • 当执行到 const data = await fetchData('https://api.example.com/data') 时,getData 函数会暂停执行,并等待 fetchData 返回的 Promise 完成。
  3. 继续执行其他代码

    • fetchDataPromise 完成之前,JavaScript 引擎不会等待,而是继续执行后面的代码。
    • 因此,console.log('This message is printed immediately after calling getData') 会立即执行,并打印消息。
  4. fetchData 完成

    • 2秒后,fetchData 返回的 Promise 完成,await 表达式返回 Promise 的解析值。
    • getData 函数继续执行,打印 data 的内容。
Fetching data...
This message is printed immediately after calling getData
Data from https://api.example.com/data

更详细的解释

  • getData 函数启动

    • console.log('Fetching data...') 执行并打印 Fetching data...
  • 暂停等待 fetchData

    • await fetchData('https://api.example.com/data') 会暂停 getData 函数的执行,并等待 fetchData 返回的 Promise
    • fetchData 完成之前,getData 函数的其余部分不会执行。
  • 继续执行其他代码

    • getData 函数暂停后,JavaScript 引擎继续执行 getData 函数后面的代码,打印 This message is printed immediately after calling getData
  • fetchData 完成

    • 2秒后,fetchData 返回的 Promise 完成,await 表达式返回 Promise 的解析值。
    • const data = await fetchData('https://api.example.com/data') 赋值完成,data 变量包含 fetchData 的返回值。
    • console.log(data) 执行并打印 Data from https://api.example.com/data

关键点

  • await 的作用:暂停当前异步函数的执行,等待 Promise 完成,并返回结果。其他代码不会受影响,会继续执行。
  • 代码的执行顺序:在 await 暂停期间,其他同步代码会继续执行,不会阻塞主线程。
  • 异步函数的可读性asyncawait 提供了更直观的异步处理方式,使得异步代码看起来像同步代码,但实际行为仍然是异步的。
  • 23
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值