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解析之前执行,但它没有。
process.nextTick
回调安排在当前“阶段”结束时。 docs 将 phase 定义为事件循环期间一个 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
已执行一次)时,我们有这样的状态:
() => console.log('tick')
正在等待当前 phase() => console.log('promise resolved')
正在承诺作业队列中等待test
正在承诺作业队列中等待(第二个条目)当同步部分结束时,引擎准备进入事件循环并处理下一阶段。但是,在转换到下一阶段之前,会执行刻度回调。所以这解释了“勾选”是第一位的。
在事件循环中的某个时刻,我们进入 Promise 作业阶段(上面的简化图中没有具体指出):这是输出第一个“Promise returned”,然后执行
test
(第二次执行)的地方。
再次
test
会做类似的事情:
() => console.log('tick')
正在等待当前 phase() => console.log('promise resolved')
正在承诺作业队列中等待但是现在我们应该意识到我们仍然处于 Promise 作业阶段,并且在该阶段发现 Promise 作业队列仍然不为空(我们只是在那里添加了一个新作业),因此没有过渡到下一个作业阶段,因此现在还不是“打勾”的时候。
所以这一次,首先输出'promiseresolved',然后这个phase结束,因为promise作业队列中没有更多的promise作业:现在是打勾的时间,输出'tick'。
tick 的概念以及在当前
phase之后执行的
nextTick
回调不是 ECMA 脚本语言规范的一部分。
就我个人而言,我不会打电话给
nextTick
。