我在nodeJS遇到了一个相当奇怪的问题,我无法弄清楚原因。
请考虑以下代码:
(async () => {
console.log ("1");
await new Promise ((resolve, reject) => {
setTimeout (() => {
console.log ("2");
resolve ();
}, 1000);
});
console.log ("3");
process.exit ();
})();
此代码完全按照预期执行。它按此顺序打印123
。打印1
后,它等待大约一秒钟。完善。现在让我们看看以下示例:
const fs = require ("fs");
(async () => {
const stream = fs.createWriteStream ("file.txt");
stream.write ("Test");
console.log ("1");
await new Promise ((resolve, reject) => {
stream.on ("finish", () => {
console.log ("2");
resolve ();
});
});
console.log ("3");
process.exit ();
})();
根据我的理解,此代码应该完成,或者 - 如果finish
事件永远不会被触发 - 无限运行。恰恰相反:它打印1
,然后退出。不应该至少在退出之前打印另一个3
,因为这是脚本的结尾?
重要提示:我知道承诺无法解决,因为未在流上调用.end()
。我想知道脚本为什么会完成。
有人可以向我解释这种行为吗?
答案 0 :(得分:1)
最好的解释可能是在没有async/await
关键字的情况下写这个,并且你要承担这些不做任何“神奇”的事情,并且仅仅是“糖”用于解决Promise的不同方式而不是.then()
。
const fs = require ("mz/fs");
const stream = fs.createWriteStream("file.txt");
stream.write("Test");
console.log("1");
new Promise ((resolve, reject) => {
stream.on ("finish", () => {
console.log("2");
resolve();
});
}).then(() => {
console.log("2");
process.exit();
});
这是完全相同的事情吧!那么捕获的地方在哪里。
你真正缺少的东西是“没有”,当你打开一个文件句柄时,“必须”在程序退出之前显式关闭。因此,“没有什么可以等待”,程序完成但没有“分支”到仍在等待Promise
到resolve()
的部分。
它只记录"1"
的原因是因为剩下的分支“等待”Promise要解决,但是在程序结束之前它永远不会到达那里。
当然,当您实际在stream.end()
之后立即调用write
或理想地“等待”任何可能待处理的写入请求时,所有更改都会发生变化:
const fs = require ("mz/fs");
(async () => {
const stream = fs.createWriteStream ("file.txt");
await stream.write ("Test"); // await here before continuing
stream.end()
console.log ("1");
await new Promise ((resolve, reject) => {
stream.on ("finish", () => {
console.log ("2");
//resolve ();
});
});
console.log ("3");
//process.exit ();
})();
当然,这将记录列表中的每个输出,您应该知道。
因此,如果您希望在日志中看到"3"
,那么它之所以不是因为我们不会关闭流的await
。再次可能最好通过摆脱await
:
const fs = require ("mz/fs");
(async () => {
const stream = fs.createWriteStream ("file.txt");
await stream.write ("Test");
stream.end()
console.log ("1");
new Promise ((resolve, reject) => { // remove await - execution hoisted
stream.on ("finish", () => {
console.log ("2");
//resolve ();
});
});
console.log ("3");
//process.exit ();
})();
然后你“应该”看到:
1
3
2
至少在大多数系统上,除非你有“极端”滞后。但一般情况下"finish"
应在“等待”write
之后到达下一行之前被解雇。
注意:只需在此处使用
mz
库来演示await
方法上的write()
,而不包含回调。一般来说,回调执行应该解决相同的问题。