Home > Net >  Why need to new a promise when reading streams?
Why need to new a promise when reading streams?

Time:08-05

ok i saw this example of reading a stream and returning a promise using new Promise.

function readStream(stream, encoding = "utf8") {
    
    stream.setEncoding(encoding);

    return new Promise((resolve, reject) => {
        let data = "";
        
        stream.on("data", chunk => data  = chunk);
        stream.on("end", () => resolve(data));
        stream.on("error", error => reject(error));
    });
}

const text = await readStream(process.stdin);

My question is why "new Promise" ? can i do it in the 2nd version like

function readStream(stream, encoding = "utf8") {
    
    stream.setEncoding(encoding);

        let data = "";
        
        stream.on("data", chunk => data  = chunk);
        stream.on("end", () => Promise.resolve(data));
        stream.on("error", error => Promise.reject(error));

}

const text = await readStream(process.stdin);

Haven't tried it yet, but basically want to avoid the new keyword.


some updates on the 2nd version, since async functions always return a Promise.

A function/method will return a Promise under the following circumstances:

You explicitly created and returned a Promise from it's body. You returned a Promise that exists outside the method. You marked it as async.

const readStream = async (stream, encoding = "utf8") => {
    
    stream.setEncoding(encoding);

        let data = "";
        
        stream.on("data", chunk => data  = chunk);
        stream.on("end", () => Promise.resolve(data));
        stream.on("error", error => Promise.reject(error));
}

const text = await readStream(process.stdin);

How's this 3rd version ?

CodePudding user response:

If you want readStream to return a promise, you'll have to ... return a promise for readStream (returning a promise in some callback is not doing that).

What the first code is doing, is promisifying the stream API. And that's exactly how it should be done.

The second version of the code is based on a misunderstanding: it seems to hope that returning a promise in the callback passed to the stream.on method, will somehow make readStream return that promise. But when the on callback is called, readStream has already returned. Since readStream has no return statement, it already returned undefined and not a promise.

As a side note, when the stream API calls the callback you passed to the on method, it does not even look at the returned value -- that is ignored.

The third version is an async function, so it now is guaranteed the function will return a promise. But as the function still does not execute a return statement, that promise is immediately resolved with value undefined. Again, the returned values in the callbacks are unrelated to the promise that the async function has already returned.

new keyword

If you want to avoid the new keyword, then realise that anything that can be done with promises can also be done without them. In the end promises are "only" a convenience.

For instance, you could do:

function readStream(stream, success, failure, encoding="utf8") {
    let data = "";
    stream.setEncoding(encoding);
    stream.on("data", chunk => data  = chunk);
    stream.on("end", () => success(data));
    stream.on("error", failure);
}

function processText(text) {
    // ... do something with text 
}

function errorHandler(error) {
    // ... do something with the error
}

readStream(process.stdin, processText, errorHandler);

CodePudding user response:

You need to construct and return a Promise so that the consumer of the function has something to hook into the asynchronous action being performed. (Another option would be to define the function to also take a callback as an argument.)

If you try to do it the way you're doing with the second snippet, readStream will not return anything, so await readStream(process.stdin); will resolve immediately, and it'll resolve to undefined.

Doing

stream.on("end", () => Promise.resolve(data));

and

stream.on("error", error => Promise.reject(error));

constructs new Promises at that point in the code, but you need the consumer of the function to have access to the Promise that resolves (or rejects) - and so you must have return new Promise at the top level of the function.

  • Related