一道事件环面试题引发的思考

直接上代码,想想下面的执行顺序

let fs = require('fs');
setTimeout(function(){
    Promise.resolve().then(()=>{
        console.log('XXX2');
    })
},0);
Promise.resolve().then(()=>{
    console.log('XXX1');
});
fs.readFile('XXX',function(){
    process.nextTick(function(){
        console.log('nextTick')
    })
    setImmediate(()=>{
        console.log('setImmediate')
    });
});
复制代码

执行结果为

XXX1, XXX2, nextTick, setImmediate 
复制代码

仔细想想为什么顺序是这样呢?

1: 首先我们要搞清楚Node.js 的Event Loop 到底是什么东西?

大家都知道JavaScript是单线程的,为什么是单线程的,因为要用 Javascript操作DOM,不可能对DOM操作加锁,这样使得JavaScript更加复杂,想想Java里面的并发包,就知道多线程环境并发操作是有多复杂了。

Node.js就是要让Javascript变为多线程的操作,所以就有了Event Loop,用来处理各种类型的回调函数,就是个执行非阻塞IO的一个操 作。既然都是多线程操作了,肯定就有线程调度和队列了。队列很 好理解,执行先进先出的回调函数的一个集合。

Node.js里有一个libuv,是一个高性能,事件驱动的IO跨平台的API 集合,是用C/C++写的,里面就是处理Event Loop的,调用操作系统的 API执行非阻塞IO操作。其实libuv底层还是有一些处理多线程所用到 的同步原语(互斥锁,读/写锁,信号量,内存屏障),还好我学过Java, 对这些都是非常了解的,尽管它是C++实现的。

2: 第二步,需要理解什么是microtask,什么是macrotask ?

为什么要有microtask和macrotask呢,是因为线程要调度嘛,把线程 调度的不同形态分成了两类,一类是microtask,一类是macrotask。 而在Node.js中,有很多是用来协调异步任务的,例如:setTimeout, setImmediate,process.nextTick,setInterval,Promise.resolve.t hen()。Node.js里规定microtask是比macrotask先执行的。 附上一张运行顺序图:

   ┌───────────────────────────────────┐
┌─>│timers(计时器)执行                  │
│  |setTimeout以及setInterval的回调     | ----- 1
│  └──────────┬────────────────────────┘
│  ┌──────────┴────────────┐
│  │     I/O callbacks     │
│   处理网络,流,TCP的错误  │  ----------------2
│  │      callback      │
│  └──────────┬────────────┘
│  ┌──────────┴────────────┐
│  │     idle, prepare     │
│  │    node内部使用        │-------------------3
│  └──────────┬────────────┘      ┌───────────────┐
│  ┌──────────┴────────────┐      │   incoming:   │
│  │poll(轮询)            │<─────┤  connections, │
│  │ 执行poll中的i/o队列检查 │      │data, etc.    │-----4
│  │定时器是否到时          │      └───────────────┘
│  └──────────┬────────────┘          
│  ┌──────────┴────────────┐      
│  │        check          │
│  │  存放setImmediate回调  │-------------------5
│  └──────────┬────────────┘
│  ┌──────────┴────────────┐
└──┤    close callbacks    │
  │ 关闭的回调例如         │-------------------6
  │ socket.on('close')    │
  └───────────────────────┘
复制代码

在上面的图中,我们只需考虑1,4,5的时期,在最上面代码中,Promise.resolve().then()属于microTask,而setTimeout和setImmediate属于macroTask,显然,问题就迎刃而解了。

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值