10分钟理解下js中的event loop事件循环机制

吐槽君 分类:javascript

1.js为什么是单线程的?

每一种语言都有它的运行机制,javascript也不会例外。js最大的特点就是单线程,这是因为js在设计之初就是作为浏览器的脚本语言,主要作用就是处理用户交互,在同一时间只能做同一件事,用于操作DOM。设想,如果js是多线程的,那就会带来很多复杂的同步问题了,势必会影响浏览器的交互效果,不妨假设一下若js有两个线程,A线程需要将页面信息删除,B线程需要将页面信息修改显示,那就会让前端处理逻辑变得复杂,也就会影响与用户的交互体验了。

2.js的事件循环机制

js是一门单线程的语言,而它的异步和多线程则是通过EventLoop来实现的,大体有三个部分:
1.调用栈(call stack)
2.消息队列(Message Queue)
3.微任务队列(Microtask Queue)
1618282251.png
eventloop开始时会从全局代码开始一行一行执行,遇到函数调用会把它压入调用栈中,被压入的函数叫做 (Frame):
1618283044(1).png
当函数执行完毕后会从调用栈中弹出:
1618283286(1).png

3.js代码测试

3.1.普通函数的调用

function func1() {
  console.log(1);
}
function func2() {
  console.log(2);
  func1()
  console.log(3);
}
func2()
// 执行顺序 2 => 1 => 3
 

分析:
1.首先把func2的调用压入调用栈中,执行它里面的代码,console.log(2);被压入栈中并执行,打印出2弹出;
2.接下来func1的调用被压入调用栈中,执行它里面的代码,console.log(1);被压入栈中并执行,打印出1弹出,func1执行完毕弹出;
3.最后console.log(3);被压入栈中并执行,打印出3弹出,func2执行完毕弹出,到这里整个调用栈被清空。

3.2.普通函数+定时器的调用

js中的异步操作:ajax、事件回调、setTimeout、setInterval中的回调函数会入队到消息队列中成为消息,消息会在调用栈清空的时候执行。

function func1() {
  console.log(1);
}
function func2() {
  setTimeout(() => {
    console.log(2);
  }, 0);
  func1()
  console.log(3);
}
func2()
// 执行顺序 1 => 3 => 2
 

分析:
1.首先把func2的调用压入调用栈中,执行它里面的代码,setTimeout定时器会被压入栈时,她里面的回调函数会入队到消息队列中,消息会在调用栈清空的时候执行,此时定时器弹出;
2.接下来func1的调用被压入调用栈中,执行它里面的代码,console.log(1);被压入栈中并执行,打印出1弹出,func1执行完毕弹出;
3.console.log(3);被压入栈中并执行,打印出3弹出,func2执行完毕弹出,到这里整个调用栈被清空;
4.最后因为调用栈为空,消息队列中的消息就会被压入调用栈中执行,
console.log(2);打印出2弹出。

3.3.promise+定时器的调用

promise、async await 创建的异步操作会加入到微任务队列中,它会在调用栈被清空的时候立即执行,并且处理期间,新加入的微任务也会一同执行。

var p = new Promise(resolve => {
  console.log(4);
  resolve(5)
})
function func1() {
  console.log(1);
}
function func2() {
  setTimeout(() => {
    console.log(2);
  }, 0);
  func1()
  console.log(3);
  p
    .then(resolved => {
      console.log(resolved);
    })
    .then(() => {
      console.log(6);
    })
}
func2()
// 执行顺序 4 => 1 => 3 => 5 => 6 => 2
 

分析:
1.promise本身是立即执行函数,new Promise首先被压入调用栈中,之后console.log(4);resolve(5);分别被压入栈中并执行,然后一一弹出,最后弹出new promise这个构造函数;
2.接下来func2的调用被压入调用栈中,执行它里面的代码,setTimeout定时器会被压入栈中,她里面的回调函数会在调用栈被清空的时候执行,此时定时器被弹出;
3.紧接着func1的调用被压入调用栈中,执行它里面的代码console.log(1);,它会被压入栈中并执行,打印出1弹出,func1执行完毕弹出;
3.console.log(3);被压入栈中并执行,打印出3弹出;
4.func2执行完毕弹出,到这里整个调用栈被清空;
5.之后promise后边的两个then的回调函数,会按照先后顺序入队到微任务队列中,调用栈中的then的回调函数会一一弹出,调用栈为空;
6.此时调用栈为空,所以会立即执行微任务队列中的任务,打印出5并弹出,然后打印6弹出;
7.最后,压入并执行消息队列中的消息,
console.log(2);打印出2弹出。

回复

我来回复
  • 暂无回复内容