浏览器中js执行机制学习笔记

浏览器中js执行机制学习笔记

RiverSouthMan
0.0772019.05.15 20:56:37 字数 872 阅读 291

同步任务

当一个脚本第一次执行的时候,js引擎会解析这段代码,并将其中的同步代码按照执行顺序加入执行栈中,然后从头开始执行。如果当前执行的是一个方法,那么js会向执行栈中添加这个方法的执行环境,然后进入这个执行环境继续执行其中的代码。当这个执行环境中的代码 执行完毕并返回结果后,js会退出这个执行环境并把这个执行环境销毁,回到上一个方法的执行环境。这个过程反复进行,直到执行栈中的代码全部执行完毕。

 
call stack

这个同步代码的执行过程可以是无限进行下去的,除非发生了栈溢出,即超过了所能使用内存的最大值。

异步任务

js引擎遇到一个异步事件后并不会一直等待其返回结果,而是会将这个事件挂起,继续执行执行栈中的其他任务。当一个异步事件返回结果后,js会将这个事件加入与当前执行栈不同的另一个队列,我们称之为事件队列。被放入事件队列不会立刻执行其回调,而是等待当前执行栈中的所有任务都执行完毕, 主线程处于闲置状态时,主线程会去查找事件队列是否有任务。如果有,那么主线程会从中取出排在第一位的事件,并把这个事件对应的回调放入执行栈中,然后执行其中的同步代码...如此反复

 
image.png

事件队列

我们先看两个实验

// 实验一
console.log('进入页面')
  
setTimeout(() => {
    console.log('setTimeout1')
}, 0)
new Promise((resolve) => {
    console.log('promise')
    setTimeout(() => {
      console.log('setTimeout2')
      resolve()
    }, 0)
}).then(() => {
    console.log('then')
})
console.log('代码执行结束')

/* 打印结果
进入页面
promise
代码执行结束
setTimeout1
setTimeout2
then
*/

  

// 实验二
console.log('进入页面')
  
setTimeout(() => {
    console.log('setTimeout1')
}, 0)
new Promise((resolve) => {
    console.log('promise')
    resolve()
}).then(() => {
    console.log('then')
})
console.log('代码执行结束')

/*打印结果
进入页面
promise
代码执行结束
then
setTimeout1
*/

  

上面的两个demo中,demo2里在Promise里使用了setTimeout包了一层。造成then方法中的回调函数执行顺序稍有不同。一个在setTimeout之后,一个在setTimeou之前。为什么会造成这样的结果?

因为事件分为 macro-taskmicro-task。不同的事件会进入不同的队列。
也就是说事件队列对应的也有两个,macro queue和 micro queue

  • macro-task(宏任务):包括整体代码script,setTimeout,setInterval
  • micro-task(微任务):Promise,process.nextTick

上文中提到当主线程闲置后,会去事件队列中查找执行回调函数,这个是有一个先后策略的,先micro queuemacro queue。而这里的promise中只有在执行了resolve()后才会向micro queue中push在then方法里写的回调函数。这也是上边两个实验中console.log('then')执行顺序不同的原因,下面是详细的执行顺序:

实验1:
// 主线程开始执行同步代码
进入页面
//遇到setTimeout1异步挂起,接着执行同步代码
promise
//遇到setTimeout2异步挂起,接着执行同步代码
代码执行结束
// 同步代码执行完毕,主线程闲置,检查micro queue为空,检查macro queue 有两个回调函数
setTimeout1
setTimeout2
//执行了resolve(),向micro queue中push回调函数
//同步代码执行完毕,主线程闲置检查micro queue,执行里边的回调函数
then

  

实验2:
// 主线程开始执行同步代码
进入页面
//遇到setTimeout1异步挂起,接着执行同步代码
promise
//执行了resolve(),向micro queue中push回调函数
代码执行结束
// 同步代码执行完毕,主线程闲置,检查micro queue发现有回调函数执行
then
// 同步代码执行完毕, 主线程闲置, 检查micro queue为空,检查macro queue发现有回调函数并执行
setTimeout1

  

扫描二维码关注公众号,回复: 8197738 查看本文章

总结

不同类型的任务会进入对应的Event Queue,比如setTimeout和setInterval会进入相同的Event Queue。

事件循环的顺序,决定js代码的执行顺序。进入整体代码(宏任务)后,开始第一次循环。接着执行所有的微任务。然后再次从宏任务开始,找到其中一个任务队列执行完毕,再执行所有的微任务。

 

猜你喜欢

转载自www.cnblogs.com/chargeworld/p/12044579.html