穿越了整个沙漠,只为寻eventloop的真相

今天主要讲一下eventloop在浏览器中与在node中运行的机制,首先,不得不来讲一下,下面几个名词的概念。javascript

Node是什么?

  • Node.js是一个基于 ChromeV8引擎的JavaScript运行环境(runtime),Node不是一门语言,而是让js运,行在后端的运行时,而且不包括javascript全集,由于在服务端中不包含DOM和BOM,Node也提供了一些新的模块例如http,fs模块等。css

  • Node.js 使用了事件驱动、非阻塞式 I/O 的模型,使其轻量又高效而且Node.js 的包管理器 npm,是全球最大的开源库生态系统。java

Node的优点

  • 高并发,是指在同一时间并发访问服务器node

  • I/O密集指的是文件操做、网络操做、数据库,相对的有CPU密集,CPU密集指的是逻辑处理运算、压缩、解压、加密、解密ajax

进程与线程

谈谈浏览器

  • 用户界面-包括地址栏、前进/后退按钮、书签菜单等数据库

  • 浏览器引擎-在用户界面和呈现引擎之间传送指令(浏览器的主进程)npm

  • 渲染引擎,也被称为浏览器内核(浏览器渲染进程)后端

  • 一个插件对应一个进程(第三方插件进程)promise

  • GPU提升网页浏览的体验(GPU进程)浏览器

渲染引擎

  • 渲染引擎内部是多线程的,内部包含两个最为重要的线程ui线程和js线程。

  • 这里要特别注意ui线程和js线程是互斥的,由于JS运行结果会影响到ui线程的结果。

  • ui更新会被保存在队列中等到js线程空闲时当即被执行。

js单线程

  • javascript在最初设计时设计成了单线程,为何不是多线程呢?

  • 若是多个线程同时操做DOM那岂不会很混乱?这里所谓的单线程指的是主线程是单线程的,因此在Node中主线程依旧是单线程的。

  • js只能是单线程的 不能两个线程同时操做一个dom

  • ui进程与js进程是互斥的,不能同时执行,执行js后空闲下来了,再去执行css

其余线程

  • 浏览器事件触发线程(用来控制事件循环,存放setTimeout、浏览器事件、ajax的回调函数)

  • 定时触发器线程(setTimeout定时器所在线程)

  • 异步HTTP请求线程(ajax请求线程)

队列&栈

  • 队列遵循:先进先出;

以下图所示:

  • 栈遵循:先进后出;

下列代码中,函数是放到栈中的,看看它的执行顺序

function fn1() {
    let a = 1;
    fn2();
    function fn2() {
        console.log(a);
        let b = 2;
        function fn3() {
            debugger;
            console.log(b);
        }
        fn3();
    }
}
fn1();
复制代码

执行结果以下图所示:

宏任务&微任务

  • 常见的(macro-task)宏任务:setTimeout setImmediate(只兼容ie) MessageChannel

  • 常见的(micro-task)微任务:promise.then (MutationObserver);

了解了以上几个名词的概念以后,下面重点来了,先看一下EventLoop在浏览器中的运行机制

浏览器中的EventLoop

  • 先会执行栈中的内容,栈中内容执行后执行微任务,微任务清空后再执行宏任务,宏任务会在栈中执行,不停的循环;
Promise.resolve('1').then(data => { console.log('第一个promise') });
setTimeout(() => {
    console.log('setTimeout1')
    Promise.resolve('2').then(data => { console.log('第二个promise') });
});
setTimeout(() => {
    console.log('setTimeout2');
});
复制代码

代码运行的结果为:

第一个promise
setTimeout1
第二个promise
setTimeout2
复制代码
1,setTimeout有一个4ms的最短期,也就是说无论你设定多少,反正最少都要间隔4ms才运行里面的回调,因此先执行了第一个promise异步回调,输出了第一个promise
2,setTimeout到运行时间了,输出了setTimeout1,
3,依次输出了第二个promise(Promise的任务会在当前事件循环末尾中执行)
4,最后,第二个setTimeout到时间了,输出了setTimeout2
复制代码

EventLoop在node中的运行机制

Node中的EventLoop

  • nextTick(微任务)是队列切换时执行的

  • setTimeout和setImmediate顺序是不固定,看node准备时间

setImmediate(() => {
  console.log('setImmediate1')
  setTimeout(() => {
    console.log('setTimeout1')
  }, 0);
})
setTimeout(()=>{
  process.nextTick(()=>console.log('nextTick'))
  console.log('setTimeout2')
  setImmediate(()=>{
    console.log('setImmediate2')
  })
},0);
复制代码

第一种状况,若是先执行了setImmediate函数,那么它的执行顺序为:

setImmediate1
setTimeout2
setTimeout1
nextTick
setImmediate2
复制代码
1,根据上图所示,若是先执行了setImmediate回调,把setImmediate1输出了,
2,接下来,会执行setTimeout回调,输出setTimeout2,
3,接着输出setTimeout1,接着微任务nextTick,
4,最后,输出setImmediate2
复制代码

第二种状况,若是先执行了setTimeout函数,那么它的执行顺序为:

setTimeout2
nextTick
setImmediate1
setImmediate2
setTimeout1
复制代码
1,根据上图所示,若是先执行了setTimeout回调,把setTimeout2输出了,
2,接下来,会执行poll轮询,那就是输出了nextTick,
3,接下来执行check队列中setImmediate函数,把setImmediate1输出了,check队列里的内容得先执行完,才能切换下一个队列,因此输出了setImmediate2,
4,最后循环到了setTimeout回调,输出了setTimeout1
复制代码

结语:

关于eventloop在不一样环境中运行的机制,首先要对上述提到的,队列&栈、微任务&宏任务要有深刻的理解,但愿这篇内容对你们有所帮助,固然也但愿你们若是有什么问题,能够随时加关注,进行讨论!

相关文章
相关标签/搜索