关于JavaScript异步编程的讨论
说真的,我刚学JS那会儿,最怕的就是异步。写个页面,想从服务器拉点数据,好家伙,页面直接空着等数据,用户点了按钮没反应,急得我直挠头。那时候用的还是XMLHttpRequest,那回调函数套得,跟俄罗斯套娃似的,一层又一层,代码缩进都快跑到屏幕外边去了。
后来总算有了Promise,感觉世界明亮了一点。至少能把“成功干啥”和“失败干啥”分开了,链式调用也比回调地狱看着顺眼。但一开始用也踩坑,忘了写.catch,错误静默吞掉,查bug查得想砸键盘。还有,我一度以为.then里return了,后面就能接着用,结果有次在.then里写了个异步操作没return新的Promise,后面接的数据直接是undefined,懵了半天。
// 坑就是这样的
getUserInfo()
.then(user => {
getOrders(user.id); // 这里没有return!
})
.then(orders => {
console.log(orders); // undefined,傻眼了吧
});
真正让我觉得“卧槽这也行”的,还是async/await。这语法糖真是甜到心里了。写异步代码跟写同步的一样,可读性飙升。但毛病也有,刚开始用的时候,动不动就写个await在forEach里面,还奇怪为啥不是按顺序执行的。后来才明白,得用for...of老老实实循环。
// 错误示范,不会按你想象的顺序等
array.forEach(async item => {
await doSomething(item);
});
// 正确姿势
for (const item of array) {
await doSomething(item);
}
还有更隐蔽的坑,就是多个异步任务,你明明可以同时发起,结果却用await一个个等,白白浪费了性能。这事儿挺神奇的,有时候你为了“安全”而写的顺序执行,反而是效率的敌人。
// 慢的做法
const result1 = await fetchData1();
const result2 = await fetchData2(); // 等1完成才开干
// 快的做法
const [result1, result2] = await Promise.all([fetchData1(), fetchData2()]); // 一起上!
现在项目里,我基本上是Promise打底,async/await写主要逻辑,碰到并发控制或者特殊需求再用更底层的Promise方法。感觉就像从手动挡换成了自动挡,还能偶尔切换手动模式飙个车。不过说一千道一万,这些东西的核心,还是得搞清楚事件循环和微任务队列。不然遇到setTimeout和Promise混在一起的面试题,照样头晕。
各位老哥在从回调过渡到Promise,再到async/await的过程中,都遇到过哪些印象深刻的坑?有没有哪次调试异步bug的经历,让你至今难忘?
