async思考
1.前言
前几天开发需求时, 遇到一个场景:做完一个任务后,页面数据不是最新任务数据,而是前一次任务数据,即总是落后一次。
考察的知识点其实是:浏览器事件循环机制,即宏任务、微任务执行顺序的理解。
场景代码浓缩如下:你能判断出来两个打印信息的先后顺序即可。
顺序肯定是:
// 上报任务完成, 拿到最新任务数据
// 拿到页面首屏数据
解决方案:修改为awaitrefresh()
你可能有疑惑,为什么前人的代码没有写await,因为后面没有代码了呀,没有需要等待执行的代码,所以就省略掉关键字了。
亲情建议:使用async/await时,如非业务场景需求,不要为了代码简短,而省略掉await关键字。
当然,这只是个引子,真正让我思考的是,async/await解决的场景问题。
2.困局与破局
Promise 是为了解决“回调地狱”现象,但你是否经常会用 Promise 写出“回调地狱”,并且感到疑惑呢?
这就是困局。
比如,下面这种代码
当然如果你写成if(){}else{} ,代码看着更惊人地像回调地狱了
当然了,使用async/await 是不会创造出这种金字塔结构的回调地狱,毕竟它的优势就是让你的异步代码看起来像同步的一样。
如何破局?
真正理解 Promise 的工作原理,那你就不会写出超过两级的 Promise 流。
你可以把 Promise 想像成一条长江,一个可线性无限延长的流程图。
所以, Promise 的真正打开方式如下:
使用 Promise 小技巧:每次你想在你的Promise流中写一个then/catch,首先确保你返回promise,然后转到最外层的promise(如果你一直遵循这个规则,那应该只有一层)并在那里添加你的then或catch。只要你在返回,你的值就会冒泡到最外层的promise。
3.扩展
针对于使用 async/await 的场景,想要进行并发请求的优化,就要考虑到错误处理。
而这增加了复杂性,并且要同时处理两种不同的范式。
示例如下:
const loadMessage = async ({ userInfoUrl, listUrl }) => {
try {
const [userInfo, list] = await Promise.all([fetch(userInfoUrl), fetch(listUrl)]);
return { userInfo, list };
} catch (err) {
handleError(err);
JavaScript中的try块会立即将这部分代码排除在许多引擎优化之外,因为代码不能再被分解成确定的片段。
换言之,在JavaScript中,同样的代码在被try块包裹的情况下会比不被包裹的情况下运行得更慢,即使它没有抛出的可能性。