浏览器/nodeJS中的EventLoop

大家都知道JS是一门单线程语言,也就意味着JS无法进行多线程,但是JS中异步的概念完全可以模拟多线程,而且效果差不到哪去

要完全理解异步,就需要了解 JS 的运行核心——事件循环(event loop)

但是在浏览器中运行JS和在nodeJS中运行还有一些差别,接下来我们就来看看这些差别在哪里

首先是浏览器中的EventLoop
在说浏览器EventLoop之前我们要先看看浏览器模型

浏览器模型.png
  • 用户界面 包括地址栏,书签栏等那些东西
  • 浏览器引擎 在用户界面,呈现引擎之间传送指令
  • 渲染引擎 也被称为呈现引擎
  • 请求(网络) 用于网络调用,比如HTTP请求
  • JS解释器 用于解析和执行JS代码
  • UI(用户界面后端) 用于绘制样式 和JS共用一个线程
  • 数据存储 浏览器需要保存的一些数据 不如Cookie

JS和css公用一个线程是因为浏览器不会同时渲染JS和css,一般都会先渲染css再执行JS

浏览器中的微任务
  then
  messageChannel
  mutationObersve

浏览器中的宏任务
  setTimeout
  setInterval

代码调用先进堆栈,堆栈是代码的总执行站,堆栈整个执行的过程中会先将微任务,宏任务放到相应的队列中,事件提出来等待触发,等到总执行站中的代码空了,会先看微任务队列中有没有,如果有就会放到总执行站中执行,然后在看宏任务队列中有没有。

浏览器EventLoop.png
setTimeout(function () {
    console.log('setTimeout')
})
Promise.resolve().then(function () {
      console.log('promise')
});
console.log('堆栈');

// 执行结果:
// 堆栈
// promise
// setTimeout

如果把微任务队列放到堆栈中执行的时候又发现了宏任务,会顺便吧微任务中的宏任务一起执行了

setTimeout(function () {
    console.log('setTimeout1')
    Promise.resolve().then(function () {
        console.log('promise')
    });
})
setTimeout(function () {
    console.log('setTimeout2');
});

// 执行结果:
// setTimeout1
// promise
// setTimeout2

然后是nodeJS运行环境中的EventLoop
nodeJS中的宏任务和微任务在浏览器的基础上有新增了几个
微任务
  then
  nextTick
  messageChannel
  mutationObersve

宏任务
  setTimeout
  setInterval
  setImmediate、

node的调用顺序中会比浏览器中的复杂一些

nodeJS EventLoop.png

看图虽然复杂,但是我们只需要关心timers计时器阶段,poll轮询阶段,check检查阶段(setImmediate回掉),clons关闭阶段以及微任务队列即可,因为处理网络,内部调用与咱们的宏任务和微任务的执行没有太大的关系
和浏览器运行环境不同的是微任务只会在阶段转化的时候才会调用,就是close关闭阶段后再执行下一阶段的时候

process.nextTick(function () {
    console.log('nextTick')
});
setImmediate(function () {
    console.log('setImmediate')
});

// 执行结果:
// nextTick
// setImmediate

如果宏任务执行的时候又发现微任务了,不会和浏览器一样顺便执行了,而是会将微任务再放到微任务队列中,等待整个阶段结束后,下一个阶段开始的时候先执行完微任务队列中的微任务

setTimeout(function () {
    console.log('setTimeout1')
    Promise.resolve().then(function () {
        console.log('promise')
    });
})
setTimeout(function () {
    console.log('setTimeout2');
});

// 执行结果:
// setTimeout1
// promise
// setTimeout2

反之亦然

process.nextTick(function () {
    console.log(1)
    setImmediate(function () {
        console.log(2);
    })
})
setImmediate(function () {
    console.log(3);
    process.nextTick(function () {
        console.log(4)
    })
})

// 执行结果:
// 1
// 3
// 2
// 4

timeout immediate 两个谁先执行不一定 取决于node的执行时间

setTimeout(function () {
    console.log('setTimeout');
})
setImmediate(function () {
    console.log('setImmediate')
});

// 执行结果:
// setTimeout
// setImmediate
// 或者:
// setImmediate
// setTimeout

但是加上i/o文件操作以后就会先执行setImmediate,因为setImmediate在i/o文件操作后面的那个阶段执行,执行完setImmediate会在下一个阶段的时候再执行setTimeout (timers 计时器执行阶段)

let fs = require('fs');
fs.readFile('./1.txt', function () {
    console.log('fs');
    setTimeout(function () {
        console.log('setTimeout');
    })
    setImmediate(function () {
        console.log('setImmediate')
    })
});

// 执行结果
// fs
// setImmediate
// setTimeout

你可能感兴趣的:(浏览器/nodeJS中的EventLoop)