Different strings with headstream and promise

Viewed 45

I use in my project a function:

function readStream(file) {

    console.log("starte lesen");

    const readStream = fs.createReadStream(file);
    readStream.setEncoding('utf8');

    return new Promise((resolve, reject) => {
        let data = "";

        readStream.on("data", chunk => data += chunk);
        readStream.on("end", () => {resolve(data);});
        readStream.on("error", error => reject(error));
    });
}

It will read an xml file with around 800 lines in. If I add:

readStream.on("end", () => {console.log(data); resolve(data);});

Then the xml data is complete. Everything is fine. But if I call now this readStream from another function:

const dpath = path.resolve(__basedir, 'tests/downloads', 'test.xml');
let xml = await readStream(dpath);
console.log(xml);

then the XML data is cut. I think 800 lines is nothing big. So what can happen that the data is cut at this position but not in the function itself.

3 Answers

I have tried it like following way, it seems working for me.

For complete running example clone node-cheat xml-streamer and run node main.js.

xml-streamer.js:

const fs = require('fs');

module.exports.readStream = function (file) {
    console.log("read stream started");
    const readStream = fs.createReadStream(file);
    readStream.setEncoding('utf8');
    return new Promise((resolve, reject) => {
        let data = "";
        readStream.on("data", chunk => data += chunk);
        readStream.on("end", () => {console.log(data); resolve(data);});
        readStream.on("error", error => reject(error));
    });
}

main.js:

const path = require('path');
const _streamer = require('./xml-streamer');

async function main() {
    const xml = await _streamer.readStream( path.resolve(__dirname, 'files', 'test.xml'));
    console.log(xml);
}

main();

P.S. In above mentioned node-cheat test xml file has 1121 lines.

Sometimes sync + async code can get a race condition when it's called in the same tick. Try using setImmediate(resolve, data) on your event handler, which will resolve on the next process tick.

Alternatively, if you're targeting node v12 or higher you can use the stream async iterator interface, which will be much cleaner for your code:

async function readStream(file) {

    console.log("starte lesen");

    const readStream = fs.createReadStream(file);
    readStream.setEncoding('utf8');

    let data = "";
    for await (const chunk of readStream) {
        out += chunk;
    }
    return out;
}

If you happens to use a modern version of node, there's fs.promises

const { promises: fs } = require('fs')

;(async function main() {
    console.log(await fs.readFile('./input.txt', 'utf-8'));
})()
Related