# 其他常见问题

  1. 【讨论题】谈一谈 Node 和 Web 的事件循环(EventLoop)机制的异同

    题目描述: 通过前面章节,我们了解到 Lerna 源码中利用了 EventLoop 机制将脚手架初始化和命令执行逻辑解耦,这是 EventLoop 的一个典型应用,然而 Node.js 的 EventLoop 和 Web 的 EventLoop 并不相同,你知道他们之间有哪些差异吗?你是否在实际项目中应用过 Node.js 的 setImmediate 和 process.nextTick 方法?如果有,请分享你在哪些场景下使用了这些方法,并指出你为什么在这种场景使用,它会带来什么好处?

    关键提炼:

    1. Node.js 事件循环:http://nodejs.cn/learn/the-nodejs-event-loop
    2. 什么是 process.nextTick:http://nodejs.cn/learn/understanding-process-nexttick
    3. 什么是 setImmediate:http://nodejs.cn/learn/understanding-setimmediate
    4. Web 和 Node.js 事件循环对比:http://www.ruanyifeng.com/blog/2014/10/event-loop.html

    # Nodejs 事件轮询 (opens new window)

    阶段: timer -> Pending I/O callbacks -> Idle, prepare -> Poll -> Check -> Close callbacks

    事件循环

    1. # timer

    ​ 定时器阶段,处理setTimeoutsetInterval的回调函数。进入这个阶段,主线程会检查当前时间是否满足定时器的条件,满足则执行回调函数,否则离开这个阶段。

    1. # Pending I/O callbacks

      ​ 除了以下操作

      • setTimeout()setInterval()的回调函数。(它们在 timer 阶段执行)

      • setImmediate()的回调函数。(它在 Check 阶段执行)

      • 用于关闭请求的回调函数,例如socket.on('close', ...)。(它是在 Close callbacks 阶段执行)

        其他的回调函数都是在这个阶段执行。

    2. # Idle, prepare

      ​ 该阶段只供libuv内部调用,可以忽略。

    3. # Poll

      ​ 这个阶段是轮询阶段,用于等待还未返回的 I/O 事件,例如服务器的回应,用户鼠标事件等等。这个阶段耗时会比较长。如果没有其他异步任务要处理(比如到期的定时器),会一直停留在这个阶段,等待 I/O 请求返回结果。

    4. # Check

      ​ 该阶段执行setImmediate()的回调函数。

    5. # Close callbacks

      ​ 该阶段执行关闭请求的回调函数。比如socket.on('close', ...)

    # 微任务 VS 宏任务

    ​ 为了解决同步问题(前一个操作耗时问题,导致不能再执行下去),JavaScript 引入了异步的概念。除了主线程上的同步操作外,JavaScript 还有个任务队列(task queue)。所有的大开销的任务我们都可以通过异步函数包裹一下,通过回调函数的方式去触发,我们只需要先把大开销的操作任务扔到任务队列中,等待主线程上的同步任务执行完毕后,再去轮询任务队列中的事件,满足执行条件的事件放到主线程上去执行。

    js将异步任务划分为微任务和宏任务,微任务会在宏任务之前执行(因为每次从主线程切换到任务队列时,都会优先遍历微任务队列,后遍历宏任务队列)。

    注意:只有微任务队列为空时,才会遍历宏任务队列,并且每次从主线程切入任务队列时,都会优先遍历一边微任务队列。

    setTimeout(() => {
      // 宏任务
      console.log(4)
    }, 0)
    
    new Promise(resolve => {
      resolve()
      console.log(1)
    }).then(data => {
      // 微任务
      console.log(3)
    })
    console.log(2)
    // 执行结果: 1 2 3 4
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    宏任务 Macro Task 微任务 Micro Task
    名称 浏览器 Node 名称 浏览器 Node
    I/O 操作 process.nextTick()(优先推荐使用queueMicroTask()) ×
    setTimeout Promise.then catch finally
    setInterval MutationObserver(接口提供了监视对 DOM 树所做更改的能力) ×
    setImmediate ×
    requestAnimationFrame
    告诉浏览器——你希望执行一个动画,并且要求浏览器在下次重绘之前调用指定的回调函数更新动画。该方法需要传入一个回调函数作为参数,该回调函数会在浏览器下一次重绘之前执行。
    ×

    # process.nextTick

    node 中特有的异步方法,属于微任务,但是在微任务中,它的执行时机是最早的,比Promise.then还早。

    # setImmediate

    node 中特有的异步操作,只支持 node 端,属于宏任务

    # setTimeout VS setImmediate

    setTimeoutsetImmediate 都是用于调度异步任务的函数,但它们在执行时机上有所不同。以下是它们的区别和使用场景:

    setTimeout

    用法:

    setTimeout(callback, delay)
    
    1

# Nodejs VS Web事件循环

# 进程&线程

线程和进程:线程时最小的执行单位,而进程有至少一个线程组成。

上次更新: 3 months ago