Search code examples
node.jsasync-awaites6-promise

Node process exits even after error handling


I have something like this written in nodejs

const someOtherOperation = async (message) => {
    try {
        await doSomeIoOperation(message);
    } catch (err) {
        something
        throw Error("Error doing someOtherOperation");
    } finally {
        await someCleanup();
    }

}


const someOperation = async (message) => {
    // something else
    await someOtherOperation(message);
    // something else
}

const main = async () => {
    let messagePromises = []
    let messages = await getMessages(); // fetching message from a message broker

    for (let message of messages) {
        messagePromises.push({ id: message.id, promise: someOperation(message) });
    }

    for (let messagePromise of messagePromises) {
        try {
            await messagePromise.promise;
        } catch (err) {
            console.log(err);
        }
    }

}

The expected behaviour is the for loop with try catch should not end even if there is a error in one of the promises. What is happening is my process is ending abruptly when i get an error in someotherOperation method , i do not understand i have a try catch at the main loop and any error propagating from the innermost function should be caught in the for loop in main function but it isn't getting caught somehow and the function just ends abruptly


Solution

  • Node.js detection of unhandled rejection is incorrect. There are specific spots in the life cycle of a rejected promise where the engine checks to see if there's a handler and it does not always wait until the last possible moment so it can miss places that we add a handler.

    In my code the place where i create a bunch of promises without any error/rejection handlers

     messagePromises.push({ id: message.id, promise: someOperation(message) }); 
    

    when i do the first await on the first promise it return rejected but during this time consider other promises have also been rejected , the Nodejs engine checks if there is a handler for these rejected promises and throws and error if no handler is present. So even though i add a try catch for these promises in a hope to handle the rejections the nodejs engine has already run a check for a handler and not getting one decided to throw a unhandled promise rejection.

    To get around my style of code what i did was

    const main = async () => {
        let messagePromises = []
        let messages = await getMessages(); // fetching message from a message broker
    
        for (let message of messages) {
            messagePromises.push({ id: message.id, 
            promise: someOperation(message).then(data=>[data,null]).catch(err=>[null,err]);
        }
    
        for (let messagePromise of messagePromises) {
            const [data,err] = await messagePromise .promise;
            if(err) {
               //some error handling code here.
            }
        }
    
    }
    

    This is a pretty weird behaviour for such a mature language.