我有一个无法解决的JS行为,我无法理解。
我正在node v8.4.0
上运行此代码。
我两次运行此代码。
第一次使用f1()
第二次与f2()
f2()
结果符合预期。首先打印“开始”,然后打印“结束”。
f1()
结果与预期不符。先打印“结束”,然后再打印“开始”。
有人可以告诉我下面代码的结果吗?
const fs = require('fs')
function f1() { return new Promise((resolve, reject) => { resolve() }) }
function f2() {
return new Promise((resolve, reject) => {
fs.readFile('/Users/adi/Downloads/profile.jpg', resolve)
})
}
async function main() {
setImmediate(() => { console.log('start') })
await f1()
console.log('end')
}
main()
//f1 output:
end
start
//f2 output:
start
end
据我所知,结果应该是“开始”,然后是“结束”。 我想念什么?
答案 0 :(得分:1)
将在带有setImmediate(() => { console.log('start') })
的队列之前检查带有已解决的Promises的队列
由于f1
会立即解析,因此setImmediate
的回调和已解析的Promise会同时添加到事件队列中,但要处于不同的阶段。已解决的承诺的优先级高于使用setImmediate
如果您使用process.nextTick
,则将以更高的优先级添加回调,然后setImmediate
和start
会在end
之前被记录
function f1() { return new Promise((resolve, reject) => { resolve() }) }
async function main() {
process.nextTick(() => { console.log('start') })
setImmediate(() => { console.log('start') })
await f1()
console.log('end')
}
main()
对于f2
,文件的读取将涉及更长的异步任务,因此setImmediat
仍将被调用。
答案 1 :(得分:1)
因此,在您的f1()
示例中,您在setImmediate()
和.then()
处理程序之间进行了立即解决的Promise竞赛,因为两者都将在下一次的事件队列中出现事件已准备好进行处理。
当两者都准备好运行时,由于setImmediate()
和promise这样的异步事物在其事件循环的node.js实现中如何工作的内部原因,一个先于另一个运行。在node.js中的事件循环内部,某些不同类型的异步操作具有顺序或优先级,如果所有等待都进行,则某些优先于其他操作。尽管可能很难完全理解其他方法,但是它非常复杂,并且主要是实现细节,而没有被规范完全记录。
在这种特定情况下,node.js中的本机承诺使用microTasks队列(显然有几个单独的microTasks队列),它们在setImmediate()
,计时器和I / O事件之类的东西之前运行。
但是,总的来说,最好不要依赖于完全理解所有内容,并且,如果您希望一件事情先于另一件事情发生,则不要让它成为node.js内部两者之间的竞争。只需使用您自己的代码对其进行编码即可强制执行所需的序列。这也使您的代码更加明显,并声明了您希望以什么顺序处理事物。
如果我阅读了您当前的代码,我会认为您故意在f1()
和setImmediate()
之间建立了竞争,并且并不在意哪个先运行,因为该代码不是声明性的且未定义所需的顺序。
有关事件循环中不同类型的异步操作的内部细节的更多信息,您可以阅读以下参考文献:
Promise.resolve().then vs setImmediate vs nextTick
Promises, Next-Ticks and Immediates— NodeJS Event Loop Part 3
Promises wiggle their way between nextTick
and setImmediate
这是上一篇参考文章的引文:
本机promise处理程序在与nextTick大致相同的微任务队列上执行,因此它们先于其他所有内容运行。纯JavaScript [promise]实现应使用nextTick进行调度。
对于您的f2()
示例,可能fs.readFile()
花费了有限的时间,因此f2()
不能立即解决,因此无法在同一时间运行setImmediate()
使得setImmediate()
在f2()
解析之前就开始运行。
答案 2 :(得分:0)
它的工作原理是这样的,因为Promise
是微任务。微任务在宏任务之前在调用堆栈的末尾执行。您可以阅读更多here