浏览器环境下的事件循环

学了又忘,忘了又学,结果发现node环境下的事件循环和浏览器环境下的事件循环也有区别,各种任务和页面绘制的顺序也要注意。:(

js主线程和任务队列

js主线程

JavaScript是一种单线程的编程语言,同一时间只能做一件事,所有任务都需要排队依次完成。如果有多个线程,

各个线程都在操作dom,这样就不行了。

JS本身不能实现异步,但是JS的宿主环境(浏览器,node)是多线程的,宿主环境通过某种方式,使得JS具备了异步的特性。

同步任务会放入执行栈进行执行,执行栈里的代码会原地执行。

任务队列

对于网络请求,定时器和事件监听等任务,他们的执行很耗时,在浏览器里,如果每次有这样的任务就给他们开一个线程,让不耗时的任务执行,这样好像确实解决了阻塞的问题,但如果这些线程也操作了dom啥的,一样不行。

于是添加一个任务队列,异步任务会先传给宿主环境,当到了合适的时机,立即将回调函数推送给任务队列,当执行栈空,从任务队列取出第一个回调函数,放在在执行栈中执行。

执行栈只要空了,它就会到任务队列去查看有没有需要取的任务,直到队列空

合适的时机:定时器时间到了,事件监听触发,resolve(‘success’)或reject(‘error’) (但是需要执行到p.then才会压入微任务队列里)

await是右结合,会执行右边的函数,阻塞下方代码执行,await后面的代码变成异步的,这些代码变成了微任务

任务

任务有同步任务和异步任务,异步任务分为宏任务和微任务。

代码有三种:

1.同步代码(js执行栈/回调栈)

2.微任务的异步代码(js引擎)

  • process.nextTick(node) 执行优先级高于Promise.then() 在同步任务后执行
  • Promise.then() catch()
  • Async/Await
  • Object.observe等

3.宏任务的异步代码(宿主环境)

  • script(代码块)
  • setTimeout,setInterval
  • setImmediate 在当前事件循环结束执行
  • IO

宏任务

宏任务是由宿主(浏览器、node)发起

image

微任务

es5之后有了Promise,js引擎不需要浏览器就能发起异步任务了。

images

执行顺序

浏览器环境下的事件循环:

回调栈(执行栈)、宿主环境、宏任务队列、微任务队列

image

同步代码在执行栈中执行,遇到宏任务和微任务会在合适的时机将回调函数推送到队列中,每当执行栈清空,都会先查看当前微任务队列,将微任务依此放入执行栈执行,微任务空就查看宏任务队列,依此放入执行栈执行。

当前栈对应的微任务在当前栈任务清空时立即执行,而不是下一个宏任务,如果当前微任务产生新的微任务,新的微任务会排在微任务队列后面。