JS事件环EventLoop的理解
本文的详解主要是自己总结,对EventLoop的理解。
关于javascript
javascript是一门单线程语言,在最新的HTML5中提出了Web-Worker,但javascript是单线程这一核心仍未改变。所以一切javascript版的"多线程"都是用单线程模拟出来的,一切javascript多线程都是纸老虎!
javascript事件循环
既然js是单线程,那就像只有一个窗口的银行,客户需要排队一个一个办理业务,同理js任务也要一个一个顺序执行。如果一个任务耗时过长,那么后一个任务也必须等着。那么问题来了,假如我们想浏览新闻,但是新闻包含的超清图片加载很慢,难道我们的网页要一直卡着直到图片完全显示出来?所以将程序任务分为两类:分别是同步任务和异步任务。
在JS的执行栈中,同步任务进入主执行栈(也可以说主线程),而异步任务进入任务队列(TaskQueue)等待执行,任务队列可以理解成一个消息队列,I/O设备完成一件事,就向任务队列添加一个事件,一旦主执行栈中所有的同步任务执行完毕,就会读取任务队列中等待的任务,并放入执行栈开始执行,其实就是执行异步任务的回调函数,所以说异步任务必须指定回调函数,主线程会不断的循环这个动作,所以这种运行机制又称为EventLoop(事件循环)。( 这里解释一下什么叫‘任务队列’:这是一个先进先出的数据结构,排在前面的事件会优先被主线程读取,但是当有定时器的时候主线程会先检查一下执行时间。)我们画一张图来理解这个:
- 从上面图中看到其中有宏任务和微任务
宏任务(MacroTask):setTimeout,setInterval,setImmediate(只兼容ie)
微任务(MicroTask):Promise,vue.$nextTick(function(){})
那接下来看一段代码:
console.log('main1');
process.nextTick(function() {
console.log('process.nextTick1');
});
setTimeout(function() {
console.log('setTimeout');
process.nextTick(function() {
console.log('process.nextTick2');
});
}, 0);
new Promise(function(resolve, reject) {
console.log('promise');
resolve();
}).then(function() {
console.log('promise then');
});
console.log('main2');
复制代码
JS代码开始从上往下单线程执行:
1.console.log('main1');进入执行栈执行;
2.遇到process.nextTick将它的回调函数先放入MicroTask(微任务);
3.遇到setTimeout将它的回调函数放入MacroTask(宏任务队列);
4.在执行栈中new Promise并将.then中注册的回调放入MicroTask(微任务);
5.最后一行代码console.log('main2');会放入主执行栈执行.综上所述,这段代码的结果就是先输出main1,然后第二步第三步我们不用管它,它不是在主执行栈中,所以直接到第四步输出promise,然后主执行栈继续执行第五步输出main2。
此时主执行栈执行完毕,开始事件循环,发现在MicroTask中还有任务,开始清空微任务,第二步中我们在微任务中放入了process.nextTick所以输出process.nextTick1,在第四步中将.then的回调放入了微任务,那么微任务队列继续执行输出promise then,此时微任务队列已经清空开始事件循环宏任务队列,也就是输出第三步中的setTimeout,在输出之后发现setTimeout这个回调中还有一个process.nextTick,那么这个回调继续放入微任务队列,此时事件循环发现主执行栈中已经没有任务,那么开始执行MicroTask输出:process.nextTick2
所以这道题的答案是: //main1 promise main2 process.nextTick1 promise then setTimeout process.nextTick2
在这道题中在主进程执行完毕后,应该会调用任务队列的宏任务,但是在这之前要清空了MicorTask微任务,这与我们先说的一次事件循环的循环机制(先读取宏任务后读取微任务)相悖,所以说主进程的代码也是一个MacroTask(参考:Promises/A+规范,也就是说执行完主进程的代码后执行MicroTask这是第一个事件循环(event loop),然后又开始执行任务队列(MacroTask)和process.nextTick2属于第二个事件循环。
下图为此题分析图:
来源参考:https://blog.csdn.net/yangbo1993/article/details/79114909