JavaScript 异步编程演进史
回调时代的痛苦
早期 JavaScript 的异步操作全靠回调函数。当操作相互依赖时,嵌套层级急剧增加——这就是臭名昭著的"回调地狱":
getUser(userId, function(err, user) {
if (err) return handleError(err);
getOrders(user.id, function(err, orders) {
if (err) return handleError(err);
getProducts(orders[0].id, function(err, products) {
if (err) return handleError(err);
// 终于拿到数据...
});
});
});
Promise 的救赎
ES6 引入 Promise,将嵌套改为链式调用:
getUser(userId)
.then(user => getOrders(user.id))
.then(orders => getProducts(orders[0].id))
.then(products => console.log(products))
.catch(handleError);
async/await:同步的写法,异步的执行
ES2017 的 async/await 让异步代码看起来和同步代码一样:
async function fetchUserProducts(userId) {
try {
const user = await getUser(userId);
const orders = await getOrders(user.id);
const products = await getProducts(orders[0].id);
return products;
} catch (err) {
handleError(err);
}
}
并行执行
// 顺序执行(慢,总时间 = 所有请求之和)
const user = await getUser(id);
const settings = await getSettings(id);
// 并行执行(快,总时间 = 最慢的那个)
const [user, settings] = await Promise.all([
getUser(id),
getSettings(id),
]);
总结
async/await 是目前最符合人类直觉的异步编程方式,但底层仍是 Promise。理解 Promise 的状态机和微任务队列,才能真正掌控异步代码的执行时机。