Hi,这里是前端后花园,每天学习一个 JS 片段,涨涨知识🧀!今天带来的是JS执行长任务时如何让出控制权。
代码片段
function breakItUp () {
return new Promise(resolve => {
setTimeout(resolve, 0);
});
}
async function superLongTaks() {
console.log('开始做一些事情...');
// 异步等待breakItUp中的Promise解决
await breakItUp();
console.log('完成做一些事情,现在做更多事情...');
// 这里可以添加更多需要异步等待的代码
}
superLongTasks();
分享原因
-
JavaScript 中的“长任务”指的是那些执行时间过长,会阻止主线程响应(UI线程、事件循环)、用户交互,影响用户体验;
-
JavaScript 中的“长任务”需要分解成多个任务,以便浏览器有足够的空间来响应任何待处理的交互;
-
同时通过在多个任务执行间隙将控制权“交还”给浏览器,让其运行可能堆积的任何重要任务(页面渲染、用户输入等),保证页面交互流畅。
代码解析
-
创建 Promise:
new Promise(resolve => setTimeout(resolve, 0))
创建了一个新的Promise
对象。这个Promise
将在setTimeout
的回调函数被调用时resolve
-
设置 setTimeout:
setTimeout
的回调函数并不是在达到延迟后立即执行,而是会在当前执行栈清空、所有微任务完成后,作为下一个宏任务执行; -
控制权交还事件循环:在
await
等待Promise
解决的过程中,JavaScript 运行时将控制权交还给事件循环。此时,事件循环会检查并执行任何待处理的微任务,然后处理任何新的宏任务,比如其他setTimeout
、setInterval
、I/O 回调等。由于创建的Promise
依赖于setTimeout
,它会等待直到下一个宏任务执行周期。 -
执行 await 后代码:下一个宏任务时
promise
被resolve
后,执行后续代码逻辑
让出控制权会执行什么
在 await
等待期间,会执行所有在事件循环中排队的微任务(如 Promise.then
、MutationObserver
回调等)以及下一个宏任务队列中的任务(如 setTimeout
、setInterval
回调等)。
如果代码中没有其他微任务或宏任务等待执行,浏览器可能会利用这段时间进行页面渲染、处理其他事件等。
但是,由于 setTimeout
的特性,即使设置的延迟为 0
,Promise
的resolve
也会延迟到下一个宏任务周期,从而允许浏览器在此期间执行其他任务,实现让出控制权。
这里是JS代码片段系列,记录开发过程常见代码片段,往期精选JS代码片段: