前端学习

前端基础整理 | Javascript基础 (四)Event L

2019-06-13  本文已影响2人  格致匠心

记得第一次面鹅厂的时候,懵懂的我刚做了一两个前端项目(还是没用框架裸写的那种),没怎么学过就去面了。
面试官问我是不是有了解Promise,我在项目中有用过,就回答了解过,没想到出了一道题就让我踩进坑了:(其实这道题的根本是在考事件循环 event loop

// 问输出顺序是什么
setTimeout(function(){
    console.log(1)
});
new Promise(function(resolve){
    console.log(2)
    resolve();
}).then(function(){
    console.log(3)
});
console.log(4);

萌新的我答 2 3 1 4(虽然现在依然很萌新),实际上呢应该是2 4 3 1

一、Event Loop

Event Loop是一个执行模型,在不同的地方有不同的实现。
浏览器和nodejs基于不同的技术实现了各自的Event Loop。目前本文仅讨论浏览器的情况,以后再讨论nodejs的情况。浏览器的Event Loop在HTML5规范已经明确定义了。

二、为什么要有Event Loop?

因为,Javascript本质是单线程的,为了主线程不阻塞和实现一些类似多线程的操作,我们就需要Event Loop这个执行模型。

三、宏队列和微队列

队列我们知道,是先进先出的(FIFO),因此,异步任务的回调会依次进入任务队列,等到后续被调用。

1. 宏队列(task queue)

包括整体的代码script
包括以下的异步任务:

2. 微队列(microtask queue)

包括以下异步任务:

四、Event Loop 运行原理

一图胜千言
  1. 全局script代码执行完毕后,调用栈stack会清空;
  2. 从微队列microtask queue中取出位于队首的回调任务,放入调用栈stack中执行,执行完后microtask queue长度减1;
  3. 继续取出位于队首的任务,放入调用栈stack中执行,以此类推,直到把microtask queue中的所有任务都执行完毕。注意,如果在执行microtask的过程中,又产生了microtask,那么会加入到队列的末尾,也会在这个周期被调用执行;
  4. microtask queue中的所有任务都执行完毕,此时microtask queue为空队列,调用栈stack也为空;
  5. 取出宏队列task queue中位于队首的任务,放入Stack中执行;
  6. 执行完毕后,调用栈stack为空;
  7. 重复 2 - 6 步骤。

上面是详细分析,简单总结用大白话来说:

图片来自ssssyoki的掘金

而且要注意,对于setTimeoutsetInterval这些有计时的任务,是在计时结束后才加进任务队列的。

五、问题实操:

// 问输出顺序是什么
setTimeout(function(){
    console.log(1)
});
new Promise(function(resolve){
    console.log(2)
    resolve();
}).then(function(){
    console.log(3)
});
console.log(4);

回到文首的问题,我们用事件循环来分析:

  1. 执行宏任务整体的script代码,setTimeout 加入到宏队列,new Promise执行,输出 2,并且将 then 分发到微队列,执行console.log(4)输出4;
    宏队列: [setTimeout]
    微队列: [then]
    输出: 2 4
  2. 把所有微队列的任务都取出执行,这里只有一个then,所以输出3
    宏队列: [setTimeout]
    微队列: []
    输出: 2 4 3
  3. 取出宏队列队首 setTimeout ,执行输出 1
    宏队列: []
    微队列: []
    输出: 2 4 3 1

执行完毕,输出2 4 3 1将来会再加一个复杂一点的例子。

上一篇 下一篇

猜你喜欢

热点阅读