浏览器和Node.js中的事件环(Even Loop)

事件环介绍

event loops也就是事件循环,它是为了协调事件(event),用户交互(user interaction),脚本(script),渲染(rendering),网络(networking),用户代理(user agent)的工作而产生的一个机制。

一.浏览器中事件环

我们都知道javascript是单线程的,任务是需要一个一个按顺序执行的,如果javascript有两个线程,一个为DOM增加样式,一个却要删除DOM,这样岂不是就会很混乱。单线程可以节约内存,但是必须等待前一个任务完成后才能执行下一个任务。如上图,浏览器中的事件环图,图中包含有:

  1. heap(堆):用户主动请求而划分出来的内存区域,比如你new一个对象,就是将一个对象存入堆中,可以理解为heap存对象。

  2. stack(栈):由于函数运行而临时占用的内存区域,函数都存放在栈里。

  3. Event Loop:

    • 所有同步任务都在主线程上执行,形成一个执行栈。

    • 只要异步任务有了运行结果,就在任务队列(task queue)(队列是一个先进先出的数据结构,而栈是一个先进后出的数据结构)之中放置一个事件

    • 一旦执行栈中的所有同步任务执行完毕,系统就会读取任务队列,又将队列中的事件放到stack中依次执行,就是执行异步任务中的回调函数。这个过程是循环不断的,这就是Event Loop(事件循环);

  4. 宏任务(MacroTask)和微任务(MicroTask)

    • 浏览器中宏任务(MacroTask)方法有:setTimeout setInterval
    • 浏览器中微任务(MicroTask)方法有:Promise.then MessageChannel Promise.then MessageChannel

执行过程

* 执行完主执行线程中的任务。
* 取出Microtask Queue中任务执行直到清空。
* 取出Macrotask Queue中一个任务执行。
* 取出Microtask Queue中任务执行直到清空。
* 重复第三和第四步。
复制代码

举例说明:

console.log(1);
setTimeout(function(){
    console.log('settimeout1');
    new Promise(function(resolve,reject){
    console.log('promise');
   	resolve();
    }).then(res=>{
   console.log('promise.then');
    })
});
setTimeout(function(){
   console.log('settimeout2');
    })
console.log(2);
复制代码

在浏览器中输出结果顺序为:

1
2
settimeout1
promise
promise.then
settimeout2 
复制代码

分析:

执行栈中同步任务先执行,先走console.log(1)和console.log(2);

接着是遇到setTimeout将它们的回调函数放入MacroTask(宏任务队列);

然后将任务队列中的回调函数依次放入主执行栈中执行,console.log('settimeout1'),接着promise是立即执行,promise.then是微任务放入MicroTask中先执行;

最后执行第二个setTimeout的回调函数console.log('settimeout2');

二.node.js中事件环

even loop 当Node.js启动时,它会初始化Eventloop,处理提供的输入脚本(可能会发出异步API调用),然后开始处理Event loop。只有一个线程,那就是Event循环运行的线程。事件循环以循环顺序工作,具有不同的阶段。Node中 Event loop的操作顺序如上所示。

  1. 定时器(timers)这个阶段执行定时器队列中的回调如 setTimeout() 和 setInterval()。
  2. I/O回调(I/O callbacks)这个阶段执行几乎所有的回调。但是不包括close事件,定时器和setImmediate()的回调。
  3. 闲置,准备(idle,prepare) 这个阶段仅在内部使用,可以忽略。
  4. 轮询(poll) 等待新的I/O事件,node在一些特殊情况下会阻塞在这里
  5. 检查(check)setImmediate()的回调会在这个阶段执行
  6. 宏任务(MacroTask)和微任务(MicroTask)
  7. 关闭回调(close callbacks):关闭所有的closing handles,一些onclose事件。

执行过程

  • 在进入第一次循环之前,会先进行如下操作:1. 同步任务2.发出异步请求3.规划定时器生效的时间4.执行process.nextTick()

  • 清空当前循环内的Timers Queue(执行满足条件的setTimeout、setInterval回调),清空NextTick Queue,清空Microtask Queue。

  • 清空当前循环内的I/O Queue(执行I/O操作的回调函数),清空NextTick Queue,清空Microtask Queue。

  • 清空当前循环内的Check Queue(执行setImmediate的回调),清空NextTick Queue,清空Microtask Queue。

  • 清空当前循环内的Close Queue,清空NextTick Queue,清空Microtask Queue。

  • 进入下一循环

由此可见:nextTick优先级比promise等microtask高。setTimeout和setInterval优先级比setImmediate高。
复制代码

举例说明:浏览器中的Event Loop和node的Event Loop有所不同,将上面同样的示例代码在node环境中运行

console.log(1);
setTimeout(function(){
    console.log('settimeout1');
    new Promise(function(resolve,reject){
    console.log('promise');
   	resolve();
    }).then(res=>{
   console.log('promise.then');
    })
});
setTimeout(function(){
   console.log('settimeout2');
    })
console.log(2);
复制代码

在node环境中输出结果顺序为:

1
2
settimeout1
promise
settimeout2
promise.then
复制代码

在node环境里,执行栈会先执行完当前任务队列,也就是两个setTimeout中的回调函数执行完才会去执行微任务队列,promise.then会最后执行。

转载于:https://juejin.im/post/5b38d5a5e51d4558a3055094

你可能感兴趣的:(javascript,数据结构与算法)