事件循环


同步任务在主线程上运行会形成一个执行栈。如果碰到异步任务,比如setTimeout、onClick等等的一些操作,异步任务的执行结果将会被放入消息队列,此期间主线程不阻塞。等到主线程中的所有同步任务执行完毕,就会通过event loop在消息队列里面从头开始取,放到执行栈中执行。 Event Loop(事件循环机制)会不断的循环检测消息队列。

process.nextTick(() => {
  console.log('nextTick')
})
Promise.resolve()
  .then(() => {
    console.log('then')
  })
setImmediate(() => {
  console.log('setImmediate')
})
console.log('end')

输出:
end 
nextTick
then
setImmediate

解释

process.nextTick 和 promise.then 都属于 microtask,而 setImmediate 属于 macrotask,在事件循环的 check 阶段执行。事件循环的每个阶段(macrotask)之间都会执行 microtask,事件循环的开始会先执行一次 microtask。

微任务与宏任务

macro-task(宏任务): setTimeout,setImmediate,MessageChannel
micro-task(微任务): 原生Promise, process.nextTick, MutationObserver

macrotask 和 microtask 表示异步任务的两种分类,主要区别在于他们的执行顺序

任务包括同步任务与异步任务,所有同步任务都在主线程上执行,异步事件都在事件队列中排着队

事件循环

事件循环的责任就是查看调用栈并确定调用栈是否为空。如果调用栈为空,他就会查看消息队列来确定是否有任何挂起的回调函数等待被执行。

DOM 事件

消息队列中也包括DOM事件中的回调函数比如点击事件和键盘事件

在DOM事件里,事件监听器位于Web API 环境中等待某个事件发生(在这个例子中是点击事件),并且当该事件发生的时候,回调函数则被放置在消息队列中等待被执行。

事件循环会再次检查调用栈是否为空,如果为空的话,它会把事件回调压入栈中,然后回调函数则被执行。

ES6 作业队列/ 微任务队列

ES6介绍了一种被JavaScript 中Promises使用的叫做作业队列/微任务队列的概念。消息队列和作业队列的区别就在于作业队列会比消息队列拥有更高的优先级,也就是说作业队列/微任务队列中的Promise的任务会比消息队列中的回调函数先执行。

示例一

console.log('Script start');

setTimeout(() => {
	console.log('setTimeout 1');
},0);
setTimeout(() => {
	console.log('setTimeout 2');
},0);


new Promise((resolve,reject) => {
	resolve('Promise 1 resolved');
}).then(res => console.log(res))
.catch(err => console.log(err));

new Promise((resolve,reject) => {
	resolve('Promise 2 resolved');
}).then(res => console.log(res))
.catch(err => console.log(err));


console.log('Script End');

输出

Script start
Script End
Promise 1 resolved
Promise 2 resolved
setTimeout 1
setTimeout 2

示例二

console.log('Script start');

setTimeout(() => {
  console.log('setTimeout');
}, 0);

new Promise((resolve, reject) => {
    resolve('Promise 1 resolved');
  }).then(res => console.log(res));
  
new Promise((resolve, reject) => {
  resolve('Promise 2 resolved');
  }).then(res => {
       console.log(res);
       return new Promise((resolve, reject) => {
         resolve('Promise 3 resolved');
       })
     }).then(res => console.log(res));
console.log('Script End');

输出

Script start
Script End
Promise 1 resolved
Promise 2 resolved
Promise 3 resolved
setTimeout

所以所有在微任务队列中的任务都将在消息队列中的任务之前执行。也就是说,事件循环将会在执行任何消息队列的回调之前,首先清空微任务队列中的任务。

参考:
https://segmentfault.com/a/1190000017314417
https://blog.csdn.net/qq_31687021/article/details/88920279

posted @ 2021-01-25 23:39  LucasLin  阅读(361)  评论(0编辑  收藏  举报