js事件循环详解

x33g5p2x  于2022-02-28 转载在 其他  
字(3.3k)|赞(0)|评价(0)|浏览(229)

在js事件循环中,我们需要区分环境,因为javascript可以在node环境和浏览器环境下运行的。所以我们总结js事件循环也从这两个方面去总结。

一、浏览器中js线程

浏览器是一个进程吗?它里面只有一个线程吗?
目前多数的浏览器其实是多进程的,当我们打开一个tab页面时就会开启一个新的进程,这是为了防止一个页面卡死而造成所有页面无法响应。每一个进程中又存在很多的线程,其中包括执行JavaScript代码的线程。
JavaScript的代码执行是在一个单独的线程中执行的,这也就意味着javascript的代码,在同一时刻只能做一件事,如果这个事情比较耗时的话,就意味着当前线程就会被阻塞。
所以真正耗时的操作,实际上并不是由JavaScript线程来执行的,浏览器的每一个进程是多线程的,那么其他线程可以来完成这个耗时的操作,比如网络请求,定时器,我们只需要在特定的时候执行应该有的回调即可。

二、浏览器的事件循环

上面代码执行过程如下所示:

因为在执行js代码时,会创建一个新的JavaScript线程,如果遇到定时器等异步操作时,会将该任务交给浏览器的其他线程去执行,当执行完成后,将其回调函数放入任务队列中。js在执行完主线程中的任务后,会从任务队列中提取任务来执行。

三、浏览器中的微任务和宏任务

1、宏任务有:
setTimeOut,setInternal,DOM监听,UI rendering, ajax等。
2、微任务有:
Promise.then的回调Mutation Observer ApIqueueMicrotask等。
3、执行流程
1、首先先执行主程序中的代码
2、然后在执行宏任务队列的代码之前,要先看看微任务队列中是否为空,如果不为空,则将微任务队列中的任务取出执行。
3、在执行宏任务之前,保证微任务队列中的任务为空。

四、浏览器中的事件循环面试题

setTimeout(function () {
  console.log("setTimeOut1") //六
  new Promise(function (resolve) {
    resolve()
  }).then(function () {
    new Promise(function (resolve) {
      resolve()
    }).then(function () {
      console.log("then4") //八
    })
    console.log("then2") //七
  })
})

new Promise(function (resolve) {
  console.log("promise1") //一
  resolve()
}).then(function () {
  console.log("then1") //三
})


setTimeout(function () {
  console.log("setTimeOut2") //九
})


console.log(2) //二

queueMicrotask(() => {
  console.log("queueMicrotask1") //四
})

new Promise(function (resolve) {
  resolve()
}).then(function () {
  console.log("then3") //五
})

/*
promise1
2
then1
queueMicrotask1
then3
setTimeOut1
then2
then4
setTimeOut2
*/
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")  //五

/*
script start
async1 start
async2
promise1
script end
async1 end
promise2
setTimeOut
*/

五、node事件循环

如上图所示,在node环境中通过libuv维护一个事件队列和事件循环,并且通过其中也存在线程池,通过不断轮询线程池中的任务,并将其放入事件队列中,最终再执行线程池中的任务。

六、node事件循环的图解

七、node中的微任务和宏任务

1、微任务:
next tick queue: process.nexttick
other queue: Promise的then回调函数,queueMicrotask
2、宏任务
timer queue:setTimeOut, setInternal
poll queue: I / O事件
check queue: setImmediate
close queue: close事件
3、在node中执行的顺序

1、next tick microtask queue
2、other microtask queue
3、timer queue
4、poll queue
5、check queue
6、close queue

八、node事件循环面试题

async function async1() {
  console.log("async1 start")   // 2
  await async2()
  console.log("async1 end")   // 9
}

async function async2() {
  console.log("async2")    // 3
}

console.log("script start")  // 1

setTimeout(function () {
  console.log("setTimeOut0")  // 11
}, 0)

setTimeout(function () {
  console.log("setTimeOut2")  // 13
}, 300)

setImmediate(() => {
  console.log("setImmediate")  // 12
})

process.nextTick(() => console.log("nextTick1"))   // 7 

async1()  

process.nextTick(() => console.log("nextTick2"))  // 8

new Promise(function (resolve) {
  console.log("promise1")   // 4
  resolve()
  console.log("promise2")  // 5
}).then(function () {
  console.log("promise3")   // 10
})

console.log("script end")  // 6

/*
script start
async1 start
async2
promise1
promise2
script end
nextTick1
nextTick2
async1 end
promise3
setTimeOut0
setImmediate
setTimeOut2
*/

相关文章