Nodejs 事件循环 Promise 在 process.tick 之前执行

问题描述 投票:0回答:1

const axiosTest = async () => {

    setImmediate(() => {
        console.log('immediate axios');
    })

    const x = axios.get('https://www.google.com');

    x.then((r) => console.log('fetch'));

    const x2 = new Promise(resolve => {
        resolve('promise axios')
    });

    x2.then(console.log)

    process.nextTick(() => {
        console.log('tick axios')
    });

    console.log('stack axios');
}

const normalTest = async () => {

    setImmediate(() => {
        console.log('immediate');
    })

    const x = new Promise(resolve => {
        resolve('promise')
    });

    x.then(console.log)

    process.nextTick(() => {
        console.log('tick')
    });

    console.log('stack');
}

normalTest().then(axiosTest);

执行顺序:

stack
tick
promise
stack axios
promise axios
tick axios
immediate
immediate axios
fetch

normalTest()

执行顺序:

stack
tick
promise
immediate

axiosTest()

执行顺序:

stack axios
tick axios     
promise axios  
immediate axios
fetch

当我刚刚运行 axiosTest 函数时,tick 在 Promise 解析之前执行,但是当我在 NormalTest 函数 Promise 首先解析之后运行它时。这是为什么?

我原本希望promise.tick在同一范围内的任何promise解析之前执行,但它没有。

node.js callback stack es6-promise event-loop
1个回答
0
投票

process.nextTick
回调安排在当前“阶段”结束时。 docsphase 定义为事件循环期间一个 FIFO 队列的处理:

当 Node.js 启动时,它会初始化事件循环,处理提供的输入脚本(或放入 REPL,本文档未介绍),这可能会进行异步 API 调用、安排计时器或调用 process.nextTick() ,然后开始处理事件循环。

下图显示了事件循环操作顺序的简化概述。

   ┌───────────────────────────┐
┌─>│           timers          │
│  └─────────────┬─────────────┘
│  ┌─────────────┴─────────────┐
│  │     pending callbacks     │
│  └─────────────┬─────────────┘
│  ┌─────────────┴─────────────┐
│  │       idle, prepare       │
│  └─────────────┬─────────────┘      ┌───────────────┐
│  ┌─────────────┴─────────────┐      │   incoming:   │
│  │           poll            │<─────┤  connections, │
│  └─────────────┬─────────────┘      │   data, etc.  │
│  ┌─────────────┴─────────────┐      └───────────────┘
│  │           check           │
│  └─────────────┬─────────────┘
│  ┌─────────────┴─────────────┐
└──┤      close callbacks      │
   └───────────────────────────┘

每个框将被称为事件循环的一个“阶段”。

我们可以用这个简化的脚本重现订单的变化:

const test = async () => {
    console.log('------------test-------------');
    process.nextTick(() => console.log('tick'));
    Promise.resolve().then(() => console.log('promise resolved'))
}

test().then(test)

输出为:

------------test-------------
tick
promise resolved
------------test-------------
promise resolved
tick

不同之处在于

test
是从不同的阶段执行的,这意味着不同的顺序。这是一个逐步分析,可以澄清这一点:

当主脚本完成其同步部分(即

test
已执行一次)时,我们有这样的状态:

  1. () => console.log('tick')
    正在等待当前 phase
  2. 之后执行
  3. () => console.log('promise resolved')
    正在承诺作业队列中等待
  4. test
    正在承诺作业队列中等待(第二个条目)

当同步部分结束时,引擎准备进入事件循环并处理下一阶段。但是,在转换到下一阶段之前,会执行刻度回调。所以这解释了“勾选”是第一位的。

在事件循环中的某个时刻,我们进入 Promise 作业阶段(上面的简化图中没有具体指出):这是输出第一个“Promise returned”,然后执行

test
(第二次执行)的地方。

再次

test
会做类似的事情:

  1. () => console.log('tick')
    正在等待当前 phase
  2. 之后执行
  3. () => console.log('promise resolved')
    正在承诺作业队列中等待

但是现在我们应该意识到我们仍然处于 Promise 作业阶段,并且在该阶段发现 Promise 作业队列仍然不为空(我们只是在那里添加了一个新作业),因此没有过渡到下一个作业阶段,因此现在还不是“打勾”的时候。

所以这一次,首先输出'promiseresolved',然后这个phase结束,因为promise作业队列中没有更多的promise作业:现在是打勾的时间,输出'tick'。

备注

tick 的概念以及在当前

phase
之后执行的 nextTick 回调不是 ECMA 脚本语言规范的一部分。

就我个人而言,我不会打电话给

nextTick

© www.soinside.com 2019 - 2024. All rights reserved.