面试官:你给我讲讲async/await
面试官:你给我讲讲async/await
我:嘿嘿,还好我看了八股文,自信发言:async/await我熟,不就是让异步代码看起来像同步代码嘛
面试官:不错,async/await是怎么让异步代码像同步代码的?
我:不知道哇,反正效果就这个效果,怎么做到的?
面试官:入职了我再给你讲讲,先回去等HR通知你,对了,这个简历你带回去
一、先看生活中的异步场景 🍴
餐厅点餐场景类比:
- 点单(发起请求) → 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(); }
🔑 工作原理:
- yield 暂停函数,返回 Promise → ⏸️ 状态保存
- Promise 完成后,next(data) 恢复执行 → 🔄 状态恢复
- 需手动编写执行器 → 📝 额外工作量
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之后 (微任务队列处理时执行) 完成
🔍 流程解析:
- 全局代码执行 → 🏃♂️ 主线程
- 遇到 await → 生成微任务 → ➕ 队列
- 主线程继续执行 → 输出 “全局结束”
- 主线程空闲后 → 处理微任务 → 🔄 事件循环
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 实现‘异步转同步’:
- await 暂停函数,返回 Promise → ⏸️ 状态保存
- Promise 完成后恢复执行 → 🔄 事件循环调度
- 代码顺序与逻辑顺序一致,但本质仍为异步 → 🚫 非阻塞”
理解 async/await,就是理解 JavaScript 异步编程的终极抽象! 🌟
——转载自:不爱说话郭德纲
#牛客创作赏金赛#