JavaScript执行机制(Event Loop)

JavaScript执行机制(evenLoop)

 
 

一、了解JS引擎线程


 浏览器是多进程程序,其中渲染进程中包含有JS引擎线程,负责解析与执行JS代码,也称为主线程。浏览器同时只能有一个JS引擎线程在运行JS程序,所以JS是单线程运行的。

 JavaScript的单线程,与它的用途有关。作为浏览器脚本语言,JavaScript的主要用途是与用户互动,以及操作DOM。这决定了它只能是单线程,否则会带来很复杂的同步问题。比如,假定JavaScript同时有两个线程,一个线程在某个DOM节点上添加内容,另一个线程删除了这个节点,这时浏览器应该以哪个线程为准?
 所以,为了避免复杂性,从一诞生,JavaScript就是单线程。

 那么JS单线程又是如何实现异步的呢?
 
 

二、事件循环Event Loop


事件循环(Event Loop)是JS实现异步的一种方法,也是JS的执行机制。


宏任务与微任务

 在JavaScript中,任务被分为两种,一种叫宏任务(MacroTask)也叫Task,一种叫微任务(MicroTask)也叫jobs

  • MacroTask(宏任务):包括整体代码script,setTimeout,setInterval
  • MicroTask(微任务):Promise,process.nextTick(node.js中)

 当JS执行宏任务和微任务时会将它们的回调分别放入宏队列Macrotask Queue微队列Microtask Queue中。
 

JS执行栈(调用栈)

 JS中所有的任务都会被放到执行栈等待主线程执行。JS执行栈采用的是后进先出的规则,当函数执行的时候,会被添加到栈的顶部,当执行栈执行完成后,就会从栈顶移出,直到栈内被清空。
 

事件循环执行顺序

 (1)全局代码(script)执行,主线程往调用栈里面push任务,顺序执行,当调用栈发现MacroTask(宏任务)和MicroTask(微任务)时,将它们的回调分别放入Macrotask Queue宏任务队列和Microtask Queue微任务队列中,直到执行栈清空。
 
 (2)当执行栈清空后,优先将Microtask Queue微任务队列中的任务取出放入执行栈中执行,直到Microtask Queue微任务队列清空。
 
 (3)当微任务队列执行完毕后调用栈清空,将宏任务队列里的事件放入调用栈中执行。
 
 (4)主线程不断重复上面的第三步。

注意:
 1.宏队列Macrotask Queue一次只从队列中取一个任务执行,执行完后就去执行微任务队列中的任务;
 2.微任务队列中所有的任务都会被依次取出来执行,直到Microtask Queue为空;
 3.如果在执行Microtask Queue的过程中,又产生了Microtask Queue,那么会加入到队列的末尾,也会在这个周期被调用执行。

 
事件循环如图:
 
在这里插入图片描述
 

代码分析

好了,话不多说我们来通过代码检验。
----------Talk is cheap,Show me the code.----------

console.log(1);

setTimeout(() => {
  console.log(2);
  Promise.resolve().then(() => {
    console.log(3)
  });
});

new Promise((resolve, reject) => {
  console.log(4)
  resolve(5)
}).then((data) => {
  console.log(data);
})

setTimeout(() => {
  console.log(6);
})

console.log(7);

运用上面了解到的知识,先自己做一下试试看。

好,我们来分析一下整个流程:

  • 整体script作为第一个宏任务进入主线程,遇到console.log,输出 1
  • 遇到setTimeout,其回调函数被分发到宏任务队列Macrotask Queue中。我们暂且记为setTimeout1
  • 遇到promise,new Promise直接执行,输出4,then被分发到到微任务队列Microtask Queue中。我们暂且记为then1
  • 遇到setTimeout,其回调函数被分发到宏任务队列Macrotask Queue中,记为setTimeout2
  • 遇到console.log,输出7
宏任务队列微任务队列
setTimeout1then1
setTimeout2
上表是第一轮事件循环宏任务结束时各Event Queue的情况,此时已经输出了1、4、7
接着执行then1这个微任务,输出5

好了,第一轮事件循环正式结束,这一轮的结果是输出1,4,7,5。那么第二轮时间循环从setTimeout1宏任务开始:

  • 执行console.log 输出2
  • 执行new Promise 输出3

第二轮事件循环宏任务结束,此时Event Queue

宏任务队列微任务队列
setTimeout2

第三轮事件循环开始,此时只剩setTimeout2了,执行。

  • 直接输出6。

整段代码,共进行了三次事件循环,完整的输出为1,7,4,5,2,3,6。怎么样,你做对了吗?

让我们加大难度:

console.log(1);

setTimeout(() => {
  console.log(2);
  Promise.resolve().then(() => {
    console.log(3)
  });
});

new Promise((resolve, reject) => {
  console.log(4)
  resolve(5)
}).then((data) => {
  console.log(data);
  
  Promise.resolve().then(() => {
    console.log(6)
  }).then(() => {
    console.log(7)
    
    setTimeout(() => {
      console.log(8)
    }, 0);
  });
})

setTimeout(() => {
  console.log(9);
})

console.log(10);

这道题就不分析啦,试着做下看自己有没有完全掌握。

//正确答案:
1
4
10
5
6
7
2
3
9
8

关于在浏览器里JS的执行机制Event Loop就说到这里,如果文中有错误还请各位帮忙指正,感谢!

 

本文参考:

阮一峰:JavaScript 运行机制详解:再谈Event Loop.
liuxuan:带你彻底弄懂Event Loop.
ssssyoki:这一次,彻底弄懂 JavaScript 执行机制.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值