面试官:你给我讲讲async/await

面试官:你给我讲讲async/await

我:嘿嘿,还好我看了八股文,自信发言:async/await我熟,不就是让异步代码看起来像同步代码嘛

面试官:不错,async/await是怎么让异步代码像同步代码的?

我:不知道哇,反正效果就这个效果,怎么做到的?

面试官:入职了我再给你讲讲,先回去等HR通知你,对了,这个简历你带回去

一、先看生活中的异步场景 🍴

餐厅点餐场景类比:

  1. 点单(发起请求) → 2. 厨师做菜(异步处理)
  2. 玩手机(主线程干别的) → 4. 上菜通知(回调通知)

async/await 的 “同步假象”: 就像盯着厨房等菜上桌,代码顺序和逻辑顺序一致,但实际仍为异步处理。

二、从回调函数到 async/await 的进化 ⚡

2.1 最原始的回调函数(回调地狱) 📉

function readFile(fileName, callback) {
  setTimeout(() => {
    console.log(`读取文件: ${fileName}`);
    callback(null, `文件内容: ${fileName}`);
  }, 1000);
}
readFile('a.txt', (err, data1) => {
  readFile('b.txt', (err, data2) => {
    readFile('c.txt', (err, data3) => {
      console.log('全部文件读取完成:', data1, data2, data3);
    });
  });
});

输出结果(等待 3 秒):

读取文件: a.txt  
读取文件: b.txt  
读取文件: c.txt  
全部文件读取完成: ...

🔴 问题:

  • 嵌套金字塔结构 → 📌 回调地狱
  • 错误处理繁琐 → 🔄 层层捕获
  • 执行顺序与代码顺序不一致

2.2 Promise 链式调用(异步优化) 📈

function readFilePromise(fileName) {
  return new Promise(resolve => {
    setTimeout(() => resolve(`文件内容: ${fileName}`), 1000);
  });
}
readFilePromise('a.txt')
  .then(data1 => {
    console.log('a.txt内容:', data1);
    return readFilePromise('b.txt');
  })
  .then(data2 => {
    console.log('b.txt内容:', data2);
    return readFilePromise('c.txt');
  })
  .then(data3 => {
    console.log('全部文件读取完成');
  });

输出结果(每秒一个文件):

读取文件: a.txt  
a.txt内容: ...  
读取文件: b.txt  
b.txt内容: ...  
读取文件: c.txt  
全部文件读取完成  

🔵 改进:

  • 扁平链式调用 → 🚫 避免嵌套
  • 集中错误处理 → 📍 .catch()
  • 但仍需频繁书写 .then()

2.3 async/await(“同步式” 异步) ⚡

async function readAllFiles() {
  try {
    console.log('开始读取a.txt');
    const data1 = await readFilePromise('a.txt');
    console.log('a.txt内容:', data1);
    
    console.log('开始读取b.txt');
    const data2 = await readFilePromise('b.txt');
    console.log('b.txt内容:', data2);
    
    return '全部文件读取完成';
  } catch (err) {
    console.error('读取错误:', err);
  }
}
console.log('程序开始执行');
const result = readAllFiles();
console.log('等待文件读取...');
result.then(res => console.log(res));

输出顺序(重点!):

程序开始执行  
等待文件读取...  
开始读取a.txt  (1秒后)  
a.txt内容: ...  
开始读取b.txt  (又1秒后)  
...  
全部文件读取完成  

🟢 核心优势:

  • 代码顺序即逻辑顺序 → ✅ “同步” 书写体验
  • 统一错误处理 → 📍 try/catch
  • 非阻塞执行 → 📱 UI 不卡顿

看新机会的可看看

技术大厂核心部门的一波岗,前、后端or测试机会,待遇还可以。

三、async/await 背后的原理:Generator 模拟 “同步” 🧩

3.1 Generator 函数(异步中间形态) ⏳

function* readFilesGenerator() {
  const data1 = yield readFilePromise('a.txt');
  const data2 = yield readFilePromise('b.txt');
  return '完成';
}
function runGenerator(generator) {
  const iterator = generator();
  function next(value) {
    const result = iterator.next(value);
    if (result.done) return;
    result.value.then(data => next(data));
  }
  next();
}

🔑 工作原理:

  1. yield 暂停函数,返回 Promise → ⏸️ 状态保存
  2. Promise 完成后,next(data) 恢复执行 → 🔄 状态恢复
  3. 需手动编写执行器 → 📝 额外工作量

3.2 async/await 是 Generator 的 “自动版” 🤖

// 浏览器内部简化实现
function makeAsync(func) {
  const generator = convertToGenerator(func);
  return function() {
    const iterator = generator();
    function handleResult(result) {
      if (result.done) return result.value;
      return result.value.then(
        data => handleResult(iterator.next(data)),
        err => handleResult(iterator.throw(err))
      );
    }
    return handleResult(iterator.next());
  };
}

💡 关键转换:

  • async 函数 → Generator 函数 → 🔄 状态机
  • 引擎自动管理执行流程 → 🚫 无需手动调用 next()
  • await → yield + Promise 自动解析 → 🧩 语法糖

四、为什么说 async/await 没有真正阻塞? 🔄

4.1 事件循环验证(异步本质) ⏱️

async function asyncDemo() {
  console.log('async开始');
  await Promise.resolve(); // 模拟异步
  console.log('await之后');
  return '完成';
}
console.log('全局开始');
asyncDemo().then(res => console.log(res));
console.log('全局结束');

输出顺序:

全局开始  
async开始  
全局结束  
await之后  (微任务队列处理时执行)  
完成  

🔍 流程解析:

  1. 全局代码执行 → 🏃‍♂️ 主线程
  2. 遇到 await → 生成微任务 → ➕ 队列
  3. 主线程继续执行 → 输出 “全局结束”
  4. 主线程空闲后 → 处理微任务 → 🔄 事件循环

4.2 “同步假象” 的本质 🎭

async function fetchData() {
  console.log('第一步:请求开始');
  const res = await fetch('/api'); // 异步请求
  console.log('第二步:处理响应');
  const data = await res.json();
  console.log('第三步:解析完成');
}
console.log('调用前');
fetchData().then(() => console.log('调用后'));
console.log('调用后立即执行');

执行逻辑:

 调用前 → 第一步 → 调用后立即执行 → (等待请求) → 第二步 → 第三步 → 调用后  

🌟 核心: 代码顺序 = 逻辑顺序,但非阻塞 → ✅ “伪同步” 体验

五、总结:async/await 的原理 📖

5.1 原理公式 🧮

async/await = Generator函数 + Promise自动执行器 + 事件循环调度

5.2 核心优势 🌟

代码可读性

🔴 差

🔵 中

🟢 优

错误处理

🔴 繁琐

🔵 集中

🟢 统一

阻塞主线程

❌ 否

❌ 否

❌ 否

学习成本

🔵 低

🔵 中

🟢 高(需理解原理)

5.3 面试应答模板 📝

“async/await 是 Generator 的语法糖,通过状态机和 Promise 实现‘异步转同步’:

  1. await 暂停函数,返回 Promise → ⏸️ 状态保存
  2. Promise 完成后恢复执行 → 🔄 事件循环调度
  3. 代码顺序与逻辑顺序一致,但本质仍为异步 → 🚫 非阻塞”

理解 async/await,就是理解 JavaScript 异步编程的终极抽象! 🌟

——转载自:不爱说话郭德纲

#牛客创作赏金赛#
全部评论

相关推荐

06-25 17:39
门头沟学院 Java
点赞 评论 收藏
分享
不愿透露姓名的神秘牛友
06-25 17:22
点赞 评论 收藏
分享
评论
1
1
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务