随笔 - 175  文章 - 6  评论 - 0  阅读 - 36429

题目⑥ 说一下js的执行机制--事件循环EventLoop

js是一门 单线程 语言,一切js的多线程都是用单线程模拟出来的

1. 单线程任务

同步任务、异步任务

任务队列.png

  1. 同步任务进入主线程异步任务进入Event Table并注册函数

  2. 当指定事件完成时,Event Table会将这个函数移入Event Queue

  3. 主线程内的任务执行完毕为空,会去Event Queue读取对应的函数进入主线程执行

  4. 重复上述过程,即Event Loop(事件循环)

1.1 怎么知道主线程执行栈为空?

  • js引擎存在monitoring process进程,会持续不断地检查主线程执行栈是否为空,一旦为空,就会去Event Queue检查是否有等待被调用的函数

2. 异步任务

宏任务、微任务、不同类型的任务会进入对应的Event Queue

事件循环、宏任务、微任务关系.png

  1. 事件循环的顺序,决定js代码的执行顺序

  2. 进入整体代码(宏任务)后,开始第一次循环

  3. 接着执行所有的微任务

  4. 再次执行宏任务,找到其中一个任务队列执行完毕

  5. 再执行所有微任务

2.1 宏任务macro-task

  • 整体代码script(UIrender)、setTimeout、setInterval、setImmediate、i/o
2.1.1 setTimeout
setTimeout(() => {
  task()
  console.log('延时3s')
}, 3000)
定义延时3s,实际却5、6s才执行函数,是怎么回事?

理解setTimeout的定义

  1. task()进入Event Table并注册,计时开始

  2. 执行sleep函数,计时仍在继续

  3. 3s到了,计时事件timeout完成,task()进入Event Queue,但是sleep还没执行完,事件等待

  4. sleep执行完,task()Event Queue进入主线程执行

setTimeout是经过指定时间后,把要执行的任务加入到Event Queue中,又因为单线程任务要一个一个执行,如果前面的任务需要的时间太久,就只能等待,导致真正的延迟时间大于3s

零延迟
  • 零延迟(Zero delay)并不意味着立即执行

    • 在零延迟调用setTimeout时,并不是过了给定时间间隔就马上执行回调函数

    • 其等待的时间基于队列正在等待的消息数量

setTimeout()只是将事件插入了任务队列,必须等到当前任务(执行栈)执行完,主线程才会去执行指定的回调函数。没办法保证回调函数一定会在setTimeout()指定的时间执行

总结
  • setTimeout(fn, 0)的含义是: 指定任务在主线程最早可得的空闲时间执行。也就是说,尽可能早的执行。它在任务队列的尾部添加一个事件,因此要等到主线程把同步任务和任务队列现有的事件都处理完,才会得到执行

  • 在某种程度上,我们可以利用setTimeout(fn,0)的特性,修正浏览器的任务顺序

2.1.2 setInterval
  • 对于执行顺序来说,setInterval会隔指定的时间将注册的函数置入Event Queue,如果前面的任务耗时太久,那么同样需要等待

一旦setInterval的回调函数fn执行时间超过了延迟时间ms,那么就完全看不出有时间间隔了

2.2 微任务micro-task

  • Promise、process.nextTick、mutationObserver
2.2.1 nextTick
nextTick的由来
  • 由于vue的数据驱动视图更新是异步的,即修改数据的当下,视图不会立刻更新,而是等同一事件循环中的所有数据变化完成之后,再统一进行视图更新。
nextTick的触发时机
  • 在同一事件循环中的数据变化后,DOM完成更新,立即执行nextTick(callback)内的回调。
应用场景
  • 需要在视图更新之后,基于新的视图进行操作。
简单总结事件循环
  • 同步代码执行 -> 查找异步队列,推入执行栈,执行callback1[事件循环1] -> 查找异步队列,推入执行栈,执行callback2[事件循环2]...

  • 每个异步callback,最终都会形成自己独立的一个事件循环。

结合nextTick的由来,可以推出每个事件循环中,nextTick触发的时机
  • 同一事件循环中的代码执行完毕 -> DOM 更新 -> nextTick callback触发

2.3 总结

  • 宏队列:执行每个宏队列时会先清空微任务队列

  • 微队列:微队列为空时执行宏队列

任务队列中,在每一次事件循环中,macrotask只会提取一个执行,而microtask会一直提取,直到microtask队列为空。

3. 栗子

console.log('1');
setTimeout(function() {
    console.log('2');
    process.nextTick(function() {
        console.log('3');
    })
    new Promise(function(resolve) {
        console.log('4');
        resolve();
    }).then(function() {
        console.log('5')
    })
})
process.nextTick(function() {
    console.log('6');
})
new Promise(function(resolve) {
    console.log('7');
    resolve();
}).then(function() {
    console.log('8')
})
setTimeout(function() {
    console.log('9');
    process.nextTick(function() {
        console.log('10');
    })
    new Promise(function(resolve) {
        console.log('11');
        resolve();
    }).then(function() {
        console.log('12')
    })
})

js执行机制.png

posted on   pleaseAnswer  阅读(65)  评论(0编辑  收藏  举报
编辑推荐:
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
阅读排行:
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· .NET10 - 预览版1新功能体验(一)
< 2025年3月 >
23 24 25 26 27 28 1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31 1 2 3 4 5

点击右上角即可分享
微信分享提示