浏览器环境下的事件循环
学了又忘,忘了又学,结果发现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)发起
微任务
es5之后有了Promise,js引擎不需要浏览器就能发起异步任务了。
执行顺序
浏览器环境下的事件循环:
回调栈(执行栈)、宿主环境、宏任务队列、微任务队列
同步代码在执行栈中执行,遇到宏任务和微任务会在合适的时机将回调函数推送到队列中,每当执行栈清空,都会先查看当前微任务队列,将微任务依此放入执行栈执行,微任务空就查看宏任务队列,依此放入执行栈执行。
当前栈对应的微任务在当前栈任务清空时立即执行,而不是下一个宏任务,如果当前微任务产生新的微任务,新的微任务会排在微任务队列后面。