首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >Nodejs事件循环中定时器和微任务的混淆

Nodejs事件循环中定时器和微任务的混淆
EN

Stack Overflow用户
提问于 2020-07-01 03:36:50
回答 1查看 133关注 0票数 1

我对下面代码的输出感到困惑。(取自IBM developer Nodejs课程)。

代码语言:javascript
复制
'use strict'

const logger = require('../common/logger');

const { MAINLINE, START, END } = require('../common/constants');

(function mainline() {
    logger.info(START, MAINLINE);
    
    process.nextTick(() => {
        logger.info('mainline:process.nextTick() says: hello!', 'MICROTASK')
    });

    let iteration = 0;
    let intervalTimeout = setInterval(() => {
        if (iteration < 3) {
            setTimeout((iteration) => {
                logger.info('setInterval(' + iteration + '):setTimeout() says: Timer expired!', 'TIMERS');
                process.nextTick((iteration) => {
                    logger.info('setInterval():setTimeout(' + iteration + '):process.nextTick() says: Delimit TIMERS phase!', 'MICROTASK');
                }, iteration);
            }, 0, iteration);
        } else {
            logger.info('setInterval(' + iteration + ') says: Goodbye!', 'TIMERS');
            clearInterval(intervalTimeout);
        }
        iteration++;
    });

    logger.info(END, MAINLINE)
})();

产出如下:

代码语言:javascript
复制
1593574413204:INFO: MAINLINE: START
1593574413206:INFO: MAINLINE: END
1593574413207:INFO: MICROTASK: mainline:process.nextTick() says: hello!
1593574413210:INFO: TIMERS: setInterval(0):setTimeout() says: Timer expired!
1593574413210:INFO: MICROTASK: setInterval():setTimeout(0):process.nextTick() says: Delimit TIMERS phase!
1593574413211:INFO: TIMERS: setInterval(1):setTimeout() says: Timer expired!
1593574413211:INFO: MICROTASK: setInterval():setTimeout(1):process.nextTick() says: Delimit TIMERS phase!
1593574413213:INFO: TIMERS: setInterval(2):setTimeout() says: Timer expired!
1593574413213:INFO: TIMERS: setInterval(3) says: Goodbye!
1593574413213:INFO: MICROTASK: setInterval():setTimeout(2):process.nextTick() says: Delimit TIMERS phase!

为什么记录器在最后定时器微任务之前记录“再见”?

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2020-07-02 09:10:56

也许,如果您使用递归setTimeout而不是setInterval重写此代码,这将使所发生的事情更加清晰。

这会让你觉得

代码语言:javascript
复制
(function mainline() {

  let iteration = 0;
  function intervalJob() {
    if (iteration < 3) {
      setTimeout(timeoutJob, 0, interation);
      // reproduce what setInterval does using recursive setTimeout
      setTimeout(intervalJob, 0);
    }
    else {
      logger.info('setInterval(' + iteration + ') says: Goodbye!', 'TIMERS');
    }
    iteration++;
  }
  function timeoutJob(iteration) {
    logger.info('setInterval(' + iteration + '):setTimeout() says: Timer expired!', 'TIMERS');
    process.nextTick(microtaskJobFromTimeout, iteration);
  }
  function microtaskJobFromTimeout(iteration) {
    logger.info('setInterval():setTimeout(' + iteration + '):process.nextTick() says: Delimit TIMERS phase!', 'MICROTASK');
  }

  logger.info("START", "MAINLINE");
  
  process.nextTick(() => {
    console.log('mainline:process.nextTick() says: hello!', 'MICROTASK')
  });
  
  setTimeout(intervalJob,0);
  
  logger.info("END", "MAINLINE")

})();

基本上,在iteration到达3之前,每个intervalJob调用都会安排在下一个事件循环迭代中执行两个新任务:timeoutJob (它本身将在内部调度一个微任务)和递归intervalJob

因为节点的事件循环首先执行所有超时作业,然后只执行所有微任务,所以在同一个事件循环迭代期间,最后一个intervalJob实际上将处于超时池中,而不是上一个timeoutJob

因此,通常在执行timeoutJob之前,因为它是先被排定的,然后是intervalJob,因为它也在计时器池中,最后在循环重发之前执行微任务。

您可以通过无条件地从setInterval回调进行日志记录来验证这一点,输出如下:

主线:启动

主线:结束

微任务: mainline:process.nextTick()说:你好!

INTERVALJOB:无条件intervalJob日志记录

计时器: setInterval(0):setTimeout()说:计时器过期了!

INTERVALJOB:无条件intervalJob日志记录

微任务: setInterval():setTimeout(0):process.nextTick()说:分隔定时器阶段!

计时器: setInterval(1):setTimeout()说:计时器过期了!

INTERVALJOB:无条件intervalJob日志记录

微任务: setInterval():setTimeout(1):process.nextTick()说:分隔定时器阶段!

计时器: setInterval(2):setTimeout()说:计时器过期了!

定时器: setInterval(3)说:再见!

INTERVALJOB:无条件intervalJob日志记录

微任务: setInterval():setTimeout(2):process.nextTick()说:分隔定时器阶段!

请注意,这是特定于节点的行为,在浏览器中,每个任务都有自己的微任务检查点,而计划同时触发的两个定时器实际上将在两个不同的事件循环迭代中执行。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/62669296

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档