面试官:你给我讲讲async/await
我:嘿嘿,还好我看了八股文,自信发言:async/await我熟,不就是让异步代码看起来像同步代码嘛
面试官:不错,async/await是怎么让异步代码像同步代码的?
我:不知道哇,反正效果就这个效果,怎么做到的?
面试官:入职了我再给你讲讲,先回去等HR通知你,对了,这个简历你带回去
一、先看生活中的异步场景 🍴
餐厅点餐场景类比:
- 点单 (发起请求) → 2. 厨师做菜(异步处理)
- 玩手机 (主线程干别的) → 4. 上菜通知(回调通知)
async/await 的 "同步假象": 就像盯着厨房等菜上桌,代码顺序和逻辑顺序一致,但实际仍为异步处理。
二、从回调函数到 async/await 的进化 ⚡
2.1 最原始的回调函数(回调地狱) 📉
javascript
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 秒):
makefile
读取文件: a.txt
读取文件: b.txt
读取文件: c.txt
全部文件读取完成: ...
🔴 问题:
- 嵌套金字塔结构 → 📌 回调地狱
- 错误处理繁琐 → 🔄 层层捕获
- 执行顺序与代码顺序不一致
2.2 Promise 链式调用(异步优化) 📈
javascript
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('全部文件读取完成');
});
✅ 输出结果(每秒一个文件):
makefile
读取文件: a.txt
a.txt内容: ...
读取文件: b.txt
b.txt内容: ...
读取文件: c.txt
全部文件读取完成
🔵 改进:
- 扁平链式调用 → 🚫 避免嵌套
- 集中错误处理 → 📍 .catch()
- 但仍需频繁书写 .then()
2.3 async/await("同步式" 异步) ⚡
javascript
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));
✅ 输出顺序(重点!):
less
程序开始执行
等待文件读取...
开始读取a.txt (1秒后)
a.txt内容: ...
开始读取b.txt (又1秒后)
...
全部文件读取完成
🟢 核心优势:
- 代码顺序即逻辑顺序 → ✅ "同步" 书写体验
- 统一错误处理 → 📍 try/catch
- 非阻塞执行 → 📱 UI 不卡顿
三、async/await 背后的原理:Generator 模拟 "同步" 🧩
3.1 Generator 函数(异步中间形态) ⏳
ini
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 的 "自动版" 🤖
scss
// 浏览器内部简化实现
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 事件循环验证(异步本质) ⏱️
javascript
async function asyncDemo() {
console.log('async开始');
await Promise.resolve(); // 模拟异步
console.log('await之后');
return '完成';
}
console.log('全局开始');
asyncDemo().then(res => console.log(res));
console.log('全局结束');
✅ 输出顺序:
csharp
全局开始
async开始
全局结束
await之后 (微任务队列处理时执行)
完成
🔍 流程解析:
- 全局代码执行 → 🏃♂️ 主线程
- 遇到 await → 生成微任务 → ➕ 队列
- 主线程继续执行 → 输出 "全局结束"
- 主线程空闲后 → 处理微任务 → 🔄 事件循环
4.2 "同步假象" 的本质 🎭
javascript
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 原理公式 🧮
javascript
async/await = Generator函数 + Promise自动执行器 + 事件循环调度
5.2 核心优势 🌟
特性 | 回调函数 | Promise | async/await |
---|---|---|---|
代码可读性 | 🔴 差 | 🔵 中 | 🟢 优 |
错误处理 | 🔴 繁琐 | 🔵 集中 | 🟢 统一 |
阻塞主线程 | ❌ 否 | ❌ 否 | ❌ 否 |
学习成本 | 🔵 低 | 🔵 中 | 🟢 高(需理解原理) |
5.3 面试应答模板 📝
"async/await 是 Generator 的语法糖,通过状态机和 Promise 实现'异步转同步':
- await 暂停函数,返回 Promise → ⏸️ 状态保存
- Promise 完成后恢复执行 → 🔄 事件循环调度
- 代码顺序与逻辑顺序一致,但本质仍为异步 → 🚫 非阻塞"
理解 async/await,就是理解 JavaScript 异步编程的终极抽象! 🌟
下次再见!🌈
