FileStream Promise尽早解决

时间:2018-04-24 08:22:10

标签: node.js promise stream

我在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()。我想知道脚本为什么会完成。

有人可以向我解释这种行为吗?

1 个答案:

答案 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();
});

这是完全相同的事情吧!那么捕获的地方在哪里。

你真正缺少的东西是“没有”,当你打开一个文件句柄时,“必须”在程序退出之前显式关闭。因此,“没有什么可以等待”,程序完成但没有“分支”到仍在等待Promiseresolve()的部分。

它只记录"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(),而不包含回调。一般来说,回调执行应该解决相同的问题。