js运行机制(事件循环Event Loop、宏任务与微任务、浏览器事件循环与Nodejs事件循环的区别)

系列文章目录


文章目录

  • 系列文章目录
    • 一、什么是事件循环
    • 二、宏任务与微任务
    • 三、浏览器事件循环与Nodejs事件循环的区别
      • 3.1 浏览器的EventLoop
      • 3.2Nodejs的Event Loop


一、什么是事件循环

js是单线程的,执行js代码时,遇到同步任务,直接推入调用栈中执行遇到异步任务时,将任务挂起,等到异步任务有返回之后推入到任务队列中,当所有同步任务都执行完后开始将异步任务队列一个一个推入到主线程中异步任务又分为宏任务和微任务

js运行机制(事件循环Event Loop、宏任务与微任务、浏览器事件循环与Nodejs事件循环的区别)_第1张图片
上图要表达的内容用文字来表述的话:
• 同步和异步任务分别进入不同的执行"场所",同步的进入主线程,异步的进入Event Table并注册函数。
• 当同步任务的事情完成时,Event Table会将这个函数移入Event Queue。
• 主线程内的任务执行完毕为空,会去Event Queue读取对应的函数,进入主线程执行。
上述过程会不断重复,也就是常说的Event Loop(事件循环)

二、宏任务与微任务

宏任务与微任务都表示异步耗时任务
宏任务: dom事件回调、ajax回调、定时器回调、script(整体代码)、setTimeout、setInterval、(Node的宏任务还有【setImmediate,I/O、UI交互事件】))
微任务: Promise.then catch finally(注意不是说 Promise,new promise直接执行),process.nextTick(Node),MutationObserver(监听DOM树的变化 )、Object.observe(⽤来实时监测js中对象的变化)
js运行机制(事件循环Event Loop、宏任务与微任务、浏览器事件循环与Nodejs事件循环的区别)_第2张图片

三、浏览器事件循环与Nodejs事件循环的区别

浏览器和Node 环境下,microtask 任务队列的执行时机不同

  • Node端,microtask 在事件循环的各个阶段之间执行
  • 浏览器端,microtask(微任务) 在事件循环的 macrotask (宏任务)执行完之后执行

3.1 浏览器的EventLoop

  1. js引擎将所有代码放入执行栈,并依次弹出并执行,这些任务有的是同步有的是异步(宏任务或微任务),先执行完同步任务才会执行异步任务
  2. 如果在执行栈中代码时发现宏任务则交个浏览器相应的线程去处理,浏览器线程在正确的时机(比如定时器最短延迟时间)将宏任务的消息(或称之为回调函数)推入宏任务队列。而宏任务队列中的任务只有执行栈为空时才会执行。
  3. 如果执行栈中的代码时发现微任务则推入微任务队列,和宏任务队列一样,微任务队列的任务也在执行栈为空时才会执行,但是微任务始终比宏任务先执行
  4. 当执行栈为空时,eventLoop转到微任务队列处,依次弹出首个任务放入执行栈并执行,如果在执行的过程中又有微任务产生则推入队列末尾,这样循环直到微任务队列为空。
  5. 当执行栈和微任务队列都为空时,eventLoop转到宏任务队列,并取出队首的任务放入执行栈执行。需要注意的是宏任务每次循环只执行一个
  6. 重复1-5过程
  7. 直到栈和队列都为空时,代码执行结束。引擎休眠等待直至下次任务出现

但是,浏览器的循环机制有个问题,就是没有优先级的概念,只是按照先后顺序来执行,那如果有高优先级的任务就得不到及时的执行了。 Node.js解决这些问题,它设计出来的事件循环更细致一些。

3.2Nodejs的Event Loop

Node.js 是一个新的 JS 运行环境,它同样要支持异步逻辑,包括定时器、IO、网络请求,很明显,也可以用 Event Loop 那一套来跑。浏览器的 Event Loop 只分了两层优先级,一层是宏任务,一层是微任务。但宏任务之间没有再划分优先级,微任务之间也没有再划分优先级。

而 Node.js 任务宏任务之间是有优先级的,比如定时器 Timer 的逻辑就比 IO 的逻辑优先级高,因为涉及到时间,越早越准确;而 close 资源的处理逻辑优先级就很低,因为不 close 最多多占点内存等资源,影响不大。

于是就把宏任务队列拆成了五个优先级:Timers、Pending、Poll、Check、Close。

js运行机制(事件循环Event Loop、宏任务与微任务、浏览器事件循环与Nodejs事件循环的区别)_第3张图片

Timers Callback:涉及到时间,肯定越早执行越准确,所以这个优先级最高很容易理解。
Pending Callback:处理网络、IO 等异常时的回调,有的 *niux 系统会等待发生错误的上报,所以得处理下。
Poll Callback:处理 IO 的 data,网络的 connection,服务器主要处理的就是这个。
Check Callback:执行setImmediate 的回调,特点是刚执行完 IO 之后就能回调这个。
Close Callback:关闭资源的回调,晚点执行影响也不到,优先级最低。

注意: Node.js 的 Event Loop 并不是浏览器那种一次执行一个宏任务,然后执行所有的微任务,而是执行完一定数量的 Timers 宏任务,再去执行所有微任务,然后再执行一定数量的 Pending 的宏任务,然后再去执行所有微任务,剩余的 Poll、Check、Close 的宏任务也是这样,即有优先级的宏任务

总结一下: Node.js 对宏任务做了优先级划分,从高到低分别是 Timers、Pending、Poll、Check、Close 这 5 种,也对微任务做了划分,也就是 nextTick 的微任务和其他微任务。执行流程是先执行完当前优先级的一定数量的宏任务(剩下的留到下次循环),然后执行 process.nextTick 的微任务,再执行普通微任务,之后再执行下个优先级的一定数量的宏任务。。这样不断循环。其中还有一个 Idle/Prepare 阶段是给 Node.js 内部逻辑用的,不需要关心。

例题1

console.log('begin');
//then catch finally  
process.nextTick(() => {
    console.log('nextTick');
});
Promise.resolve().then(() => {
    console.log('promise');
})
fs.readFile('./1-复习.txt', (err, data) => {
    console.log("文件读取成功");
}); // i/o 
axios.get('http://121.199.0.35:8888/user/login').then(res => {
    console.log(res.status);
}); // set fs axios 
setTimeout(() => {
    console.log('setTimeout')
}, 0);

(async function() {
    console.log('async')
})();

console.log('end');
 //输出结果 begin async end nextTick promise  settimeout 文件读取成功 200

例题2

console.log('1');
setTimeout(function () {
 	console.log('2');
		process.nextTick(function () {
		console.log('3');
	})
 new Promise(function (resolve) {
 console.log('4');
	resolve();
	}).then(function () {
	console.log('5')
	})
})
process.nextTick(function () {
 console.log('6');
});
new Promise(function (resolve) {
 console.log('7');
 resolve();
}).then(function () {
 console.log('8')
})
setTimeout(function () {
 console.log('9');
 process.nextTick(function () {
 console.log('10');
 });
 new Promise(function (resolve) {
 console.log('11');
 resolve();
 }).then(function () {
 console.log('12')
 })
});
// 1 7 6 8 2 4 3 5 9 11 10 12

参考文章:https://zhuanlan.zhihu.com/p/455906298

你可能感兴趣的:(#,js,#,nodejs,javascript,前端,ui)