欢迎来到JavaScript最烧脑也最有趣的部分!异步编程就像教你的代码"一心多用",让它能在等待时不做"木头人"。准备好迎接挑战了吗?我们将从回调地狱一路升级到async/await的优雅世界!
9.1 同步与异步概念 - 快餐店的点餐对比
同步代码就像排队点餐:
console.log("1. 排队点餐");
console.log("2. 付款并等待取餐");
console.log("3. 拿到汉堡");
console.log("4. 开始吃");
// 严格按照顺序执行,前一步没完成后面就得等着
异步代码像取号点餐:
console.log("1. 取号点餐");
setTimeout(() => {
console.log("3. 取餐铃响,去拿汉堡");
}, 1000);
console.log("2. 先找座位玩手机");
// 输出顺序:1 → 2 → 3
关键区别:
- 同步:阻塞式,一步接一步
- 异步:非阻塞,可以同时处理多个任务
9.2 回调函数模式 - 最早的"电话通知"
回调函数是最基础的异步处理方式:
function orderBurger(callback) {
console.log("下单汉堡...");
setTimeout(() => {
console.log("汉堡做好了!");
callback("汉堡");
}, 2000);
}
orderBurger((burger) => {
console.log(`吃${burger}`);
});
回调地狱的现实例子:
// 模拟点餐流程:选餐 → 付款 → 取餐 → 吃
chooseFood((food) => {
makePayment(food, (receipt) => {
pickUpFood(receipt, (meal) => {
eat(meal);
});
});
});
// 这种金字塔式代码难以阅读和维护
9.3 Promise对象 - 异步的"承诺书"
Promise解决了回调地狱问题,让异步代码更清晰:
function orderPizza() {
return new Promise((resolve, reject) => {
console.log("开始做披萨...");
setTimeout(() => {
if (Math.random() > 0.2) {
resolve("pisha"); // 成功时调用
} else {
reject("烤箱坏了!"); // 失败时调用
}
}, 1500);
});
}
orderPizza()
.then(pizza => console.log(`吃${pizza}`))
.catch(err => console.error(`失败:${err}`));
Promise链式调用:
// 模拟:登录 → 获取用户信息 → 加载好友列表
login(user)
.then(token => getUserInfo(token))
.then(user => loadFriends(user.id))
.then(friends => console.log(friends))
.catch(err => console.log(err));
9.4 async/await语法 - 异步的"同步写法"
async/await是Promise的语法糖,让异步代码看起来像同步代码:
async function dinnerTime() {
try {
console.log("1. 下单晚餐");
const burger = await orderBurger(); // 等待汉堡完成
const fries = await orderFries(); // 等待薯条完成
console.log(`3. 开始吃${burger}+${fries}`);
} catch (err) {
console.log("点餐失败:", err);
}
}
dinnerTime();
console.log("2. 等待时刷手机");
// 输出顺序:1 → 2 → 3
并行优化:
async function fastDinner() {
const [burger, fries] = await Promise.all([
orderBurger(),
orderFries()
]);
console.log(`快餐完成:${burger}+${fries}`);
}
// 同时下单汉堡和薯条,等两者都完成
9.5 事件循环机制 - JavaScript的"时间管理术"
理解事件循环是掌握异步编程的核心:
console.log("开始");
setTimeout(() => console.log("定时器"), 0);
Promise.resolve().then(() => console.log("Promise"));
console.log("结束");
/* 输出顺序:
开始
结束
Promise
定时器
*/
事件循环原理:
- 调用栈:执行同步代码
- 任务队列:微任务队列:Promise回调、MutationObserver等宏任务队列:setTimeout、setInterval、I/O等
- 事件循环不断检查:调用栈空 → 执行所有微任务 → 执行一个宏任务 → 重复
本章总结:
- 同步代码顺序执行,异步代码非阻塞
- 回调函数是最基础的异步处理方式
- Promise提供了更优雅的链式调用
- async/await让异步代码像同步代码一样易读
- 事件循环机制是JavaScript异步的核心
实战练习:
- 用Promise实现一个随机成功/失败的抽奖函数
- 使用async/await改写以下回调代码:
fetchData(data => {
processData(data, result => {
displayResult(result);
});
});
- 预测以下代码的输出顺序并解释:
console.log(1);
setTimeout(() => console.log(2), 0);
Promise.resolve().then(() => console.log(3));
console.log(4);
生活剧场:
// 模拟早餐制作流程
async function makeBreakfast() {
try {
// 并行启动咖啡机和烤面包机
const [coffee, toast] = await Promise.all([
startCoffeeMachine(),
makeToast()
]);
// 然后煎鸡蛋
const eggs = await fryEggs();
console.log(`早餐完成:${coffee}+${toast}+${eggs}`);
return true;
} catch (err) {
console.log("早餐失败:", err);
return false;
}
}
// 使用
makeBreakfast().then(success => {
if (success) {
console.log("美好的一天开始了!");
}
});
下一章我们将进入DOM操作的世界,学习如何让网页"动"起来!准备好成为网页魔术师了吗?