为何await不同Promise时浏览器有时冻结有时可正常交互?
原理核心前提
首先明确两个浏览器底层运行规则:
- JS是单线程运行,和页面GUI渲染线程互斥,只有同步代码长时间占用主线程、阻塞事件循环时,才会导致页面冻结
new Promise(executor)传入的执行器函数executor是同步立即执行的;await仅暂停当前async函数的后续执行,不会阻塞整个主线程,会让出线程给其他任务(页面渲染、用户交互、其他定时器回调等)运行,直到等待的Promise被resolve/reject后,再把后续代码作为微任务调度执行。
不同案例的具体原因解释
案例1:await 5秒后resolve的Promise不冻结
await new Promise(resolve => setTimeout(resolve, 5000));
这个Promise的执行器运行时,仅同步调用了setTimeout,把5秒后执行resolve的任务交给浏览器定时器线程,执行器本身瞬间就执行完毕了。此时await会暂停awaitPromise函数的后续执行,让出主线程,所以5秒内主线程可以正常处理页面hover、点击等交互和渲染,完全不会卡顿。5秒后定时器触发resolve,才会调度执行awaitPromise剩下的代码更新状态。
案例2:await 永远pending的空执行器Promise不冻结
await new Promise(()=>{});
这个执行器本身就是空函数,同步执行完就结束了,虽然Promise永远不会resolve,await会一直暂停awaitPromise的后续代码,但主线程已经被释放,可以正常处理其他所有任务,所以页面不会冻结,只是状态永远停在"Awaiting Promise"而已。
案例3:await 带死循环的Promise会冻结
await new Promise(()=>{ while(true){} });
这里的死循环是执行器里的同步代码,执行器运行时直接进入死循环,一直占用主线程不放,事件循环完全被阻塞,GUI渲染、用户交互所有任务都得不到执行,页面自然就冻结了。注意这个时候甚至还没走到await的暂停逻辑,卡死的原因是执行器本身的同步死循环阻塞了主线程。
你观察到的结论是完全正确的:只要Promise的执行器同步执行完毕,无论Promise是否被resolve,主线程都会被释放,GUI就不会冻结;只有同步代码长时间占用主线程才会导致页面冻结。
内容的提问来源于stack exchange,提问作者Kyle C
相关产品推荐
相关产品推荐

