console.log(‘script start’)

setTimeout(function () { console.log(‘setTimeout’)}, 0)

Promise.resolve() .then(function () { console.log(‘promise1’) }) .then(function () { console.log(‘promise2’) })console.log(‘script end’)

  1. 整体 script 作为第一个宏任务进入主线程,输出script start

  2. 遇到 setTimeout,setTimeout 为宏任务,加入宏任务队列

  3. 遇到 Promise,其 then 回调函数加入到微任务队列;第二个 then 回调函数也加入到微任务队列

  4. 继续往下执行,输出script end

  5. 检测微任务队列,输出promise1、promise2

  6. 进入下一轮循环,执行 setTimeout 中的代码,输出setTimeout

最后执行结果为:

script startscript endpromise1promise2setTimeout

经典题目 2


来看一道面试的经典题目

async function async1() { console.log(‘async1 start’) await async2() console.log(‘async1 end’)}async function async2() { console.log(‘async2’)}console.log(‘script start’)setTimeout(function () { console.log(‘setTimeout’)}, 0)async1()new Promise(function (resolve) { console.log(‘promise1’) resolve()}).then(function () { console.log(‘promise2’)})console.log(‘script end’)

  1. 整体 script 作为第一个宏任务进入主线程,代码自上而下执行,执行同步代码,输出 script start

  2. 遇到 setTimeout,加入到宏任务队列

  3. 执行 async1(),输出async1 start;然后遇到await async2(),await 实际上是让出线程的标志,首先执行 async2(),输出async2;把 async2() 后面的代码console.log(‘async1 end’)加入微任务队列中,跳出整个 async 函数。(async 和 await 本身就是 promise+generator 的语法糖。所以 await 后面的代码是微任务。)

  4. 继续执行,遇到 new Promise,输出promise1,把.then()之后的代码加入到微任务队列中

  5. 继续往下执行,输出script end。接着读取微任务队列,输出async1 end,promise2,执行完本轮的宏任务。继续执行下一轮宏任务的代码,输出setTimeout

最后执行结果为:

script startasync1 startasync2promise1script endasync1 endpromise2setTimeout

经典题目 3


我们来看下面一段代码:

setTimeout(function () { console.log(‘timer1’)}, 0)

requestAnimationFrame(function () { console.log(‘requestAnimationFrame’)})

setTimeout(function () { console.log(‘timer2’)}, 0)

new Promise(function executor(resolve) { console.log(‘promise 1’) resolve() console.log(‘promise 2’)}).then(function () { console.log(‘promise then’)})

console.log(‘end’)

  • 整体 script 代码执行,开局新增三个宏任务,两个 setTimeout 和一个 requestAnimationFrame

  • 遇到 Promise,先输出promise1, promise2,加把 then 回调加入微任务队列。

  • 继续往下执行,输出end

  • 执行 promise 的 then 回调,输出promise then

  • 接下来剩三个宏任务,我们可以知道的是timer1会比timer2先执行,那么requestAnimationFrame呢?

当每一轮事件循环的微任务队列被清空后,有可能发生 UI 渲染,也就是说执行任务的耗时会影响视图渲染的时机。

通常浏览器以每秒 60 帧(60fps)的速率刷新页面,这个帧率最适合人眼交互,大概 1000ms/60 约等于 16.7ms 渲染一帧,如果要让用户看得顺畅,单个宏任务及它相应的微任务最好能在 16.7ms 内完成。

requestAnimationFrame 是什么?

window.requestAnimationFrame() 告诉浏览器——你希望执行一个动画,并且要求浏览器在下次重绘之前调用指定的回调函数更新动画。该方法需要传入一个回调函数作为参数,该回调函数会在浏览器下一次重绘之前执行。

requestAnimationFrame 的基本思想是 让页面重绘的频率和刷新频率保持同步,相比 setTimeout,requestAnimationFrame 最大的优势是由系统来决定回调函数的执行时机。

但这个也不是每轮事件循环都会执行 UI 渲染,不同浏览器有自己的优化策略,比如把几次的视图更新累积到一起重绘,重绘之前会通知 requestAnimationFrame 执行回调函数,也就是说 requestAnimationFrame 回调的执行时机是在一次或多次事件循环的 UI render 阶段。

在我的谷歌浏览器执行结果:

promise 1promise 2endpromise thenrequestAnimationFrametimer1timer2

在我的火狐浏览器执行结果:

promise 1promise 2endpromise thentimer1timer2requestAnimationFrame

谷歌浏览器中的结果 requestAnimationFrame()是在一次事件循环后执行,火狐浏览器中的结果是在三次事件循环结束后执行。

可以知道,浏览器只保证 requestAnimationFrame 的回调在重绘之前执行,但没有确定的时间,何时重绘由浏览器决定。


参考文章


  • JavaScript 中的 Event Loop(事件循环)机制

- EOF -

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数前端工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Web前端开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
img
img
img
img
img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上前端开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新

如果你觉得这些内容对你有帮助,可以添加V获取:vip1024c (备注前端)
img

最后

正值招聘旺季,很多小伙伴都询问我有没有前端方面的面试题!

CodeChina开源项目:【大厂前端面试题解析+核心总结学习笔记+真实项目实战+最新讲解视频】

于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新**

如果你觉得这些内容对你有帮助,可以添加V获取:vip1024c (备注前端)
[外链图片转存中…(img-8Brqlwsu-1711868798064)]

最后

正值招聘旺季,很多小伙伴都询问我有没有前端方面的面试题!

CodeChina开源项目:【大厂前端面试题解析+核心总结学习笔记+真实项目实战+最新讲解视频】

前端资料图.PNG

Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐