zoukankan      html  css  js  c++  java
  • 【前端知识体系-NodeJS相关】对于EventLoop(事件轮询)机制你到底了解多少?

    EventLoop

    1. EventLoop的执行流程图

      ┌───────────────────────┐
    ┌─>│        timers         │<————— 执行 setTimeout()、setInterval() 的回调
    │  └──────────┬────────────┘
    |             |<-- 执行所有 Next Tick Queue 以及 MicroTask Queue 的回调
    │  ┌──────────┴────────────┐
    │  │     pending callbacks │<————— 执行由上一个 Tick 延迟下来的 I/O 回调(待完善,可忽略)
    │  └──────────┬────────────┘
    |             |<-- 执行所有 Next Tick Queue 以及 MicroTask Queue 的回调
    │  ┌──────────┴────────────┐
    │  │     idle, prepare     │<————— 内部调用(可忽略)
    │  └──────────┬────────────┘     
    |             |<-- 执行所有 Next Tick Queue 以及 MicroTask Queue 的回调
    |             |                   ┌───────────────┐
    │  ┌──────────┴────────────┐      │   incoming:   │ - (执行几乎所有的回调,除了 close callbacks 以及 timers 调度的回调和 setImmediate() 调度的回调,在恰当的时机将会阻塞在此阶段)
    │  │         poll          │<─────┤  connections, │ 
    │  └──────────┬────────────┘      │   data, etc.  │ 
    │             |                   |               | 
    |             |                   └───────────────┘
    |             |<-- 执行所有 Next Tick Queue 以及 MicroTask Queue 的回调
    |  ┌──────────┴────────────┐      
    │  │        check          │<————— setImmediate() 的回调将会在这个阶段执行
    │  └──────────┬────────────┘
    |             |<-- 执行所有 Next Tick Queue 以及 MicroTask Queue 的回调
    │  ┌──────────┴────────────┐
    └──┤    close callbacks    │<————— socket.on('close', ...)
    
    1. setTimeout/setInterval 属于 timers 类型;
    2. setImmediate 属于 check 类型;
    3. socket 的 close 事件属于 close callbacks 类型;
    4. 其他 MacroTask 都属于 poll 类型。
    5. process.nextTick 本质上属于 MicroTask,但是它先于所有其他 MicroTask 执行;
    6. 所有 MicroTask 的执行时机,是不同类型 MacroTask 切换的时候。
    7. idle/prepare 仅供内部调用,我们可以忽略。
    8. pending callbacks 不太常见,我们也可以忽略。

    2. 执行机制

    1. 先执行所有类型为 timers 的 MacroTask,然后执行所有的 MicroTask(注意 NextTick 要优先哦);
    2. 进入 poll 阶段,执行几乎所有 MacroTask,然后执行所有的 MicroTask;
    3. 再执行所有类型为 check 的 MacroTask,然后执行所有的 MicroTask;
    4. 再执行所有类型为 close callbacks 的 MacroTask,然后执行所有的 MicroTask;
    5. 至此,完成一个 Tick,回到 timers 阶段;
      ……
      如此反复,无穷无尽……
      执行机制

    2.1 实例理解

    const macroTaskList = [
      ['task1'],
      ['task2', 'task3'],
      ['task4'],
    ]
    
    for (let macroIndex = 0; macroIndex < macroTaskList.length; macroIndex++) {
      const microTaskList = macroTaskList[macroIndex]
    
      for (let microIndex = 0; microIndex < microTaskList.length; microIndex++) {
        const microTask = microTaskList[microIndex]
    
        // 添加一个微任务
        if (microIndex === 1) microTaskList.push('special micro task')
    
        // 执行任务
        console.log(microTask)
      }
    
      // 添加一个宏任务
      if (macroIndex === 2) macroTaskList.push(['special macro task'])
    }
    
    
    // 输出结果:
    // > task1
    // > task2
    // > task3
    // > special micro task
    // > task4
    // > special macro task
    

    2.2 执行细节分析

    2.2.1 试分析下面程序的执行结果

    console.log(1)
    setTimeout(function() {
        console.log(2)
    })
    
    Promise.resolve()
        .then(function() {
            console.log(3)
        })
    
    console.log(4)
    

    2.2.2 执行流程分析

    stack(执行栈)、Micro(微任务)、Macro(宏任务)

    1. 初始状态: stack:[], Micro: [], Macro: [script]。执行栈为空, 微任务为空, 宏任务队列中有一个整体的 script代码

    2. 主线程开始执行, 遇到console.log(1), 首先会打印 1

    3. 继续向下执行,遇到 setTimeout异步任务,就将其加入到Macro(宏任务)队列中。等待执行

    4. 继续向下执行, 遇到 Promise.resolve也是一个异步任务,单它是微任务,将其加入 Micro(微任务)队列中,等待着行

    5. 解析console.log(4), 并且打印4。 当主线程执行完打印的结果依次是 1 和 4。

    6. 这时候主线程就会问 任务(异步)队列,有没有微任务要执行,将所有的 Micro(微任务)加入执行栈执行, 打印结果 3

    7. 微任务执行完了, 就开始下一轮事件循环, 将第一个 Macro(宏任务)压入执行栈执行, 再次打印 2。

    EventLoop

    3. 谈一下宏任务与微任务的区别?(面试重点)

    [!NOTE]
    面试常考点,关键在于理解EventLoop的机制,以及宏任务和微任务的底层原理。

    3.1 宏任务

    • setTimeout
    • setInterval
    • setImmediate
    • requestAnimationFrame

    常见的宏任务: setTimeout、setInterval、setImmediate、 script中整体的代码、 I/O操作、 UI渲染等。

    3.2 微任务

    • process.nextTick
    • MutationObserver
    • Promise.then catch finally

    常见的微任务有: process.nextTick、Promise和 MutationObserver监听DOM的变化。

    3.3 微任务和宏任务的区别

    [!NOTE]

    • 微任务进入主线程执行是一队一队的, 而宏任务进入主线程是一个一个的。
    • 微任务是在主线程空闲时批量执行, 宏任务是在事件循环下一轮的最开始执行

    参考文章:

  • 相关阅读:
    从HDFS下载文件到本地
    oracle jdbc jar包异常
    spring jdbctemplate 启动报错(oracle驱动bug)
    solr搜索异常
    jdbc 处理Clob
    Wavosaur音频编辑软件: 功能专业,体积超小(500KB)
    isa server 2004中找不到HTTP过滤选项。
    cisco pix logging facility含义
    IPSec基础-密钥交换和密钥保护
    cisco 831 使用 SDM
  • 原文地址:https://www.cnblogs.com/fecommunity/p/11922251.html
Copyright © 2011-2022 走看看