Search code examples
javascriptnode.jses6-promise

setInterval and promises yields a PromiseRejectionHandledWarning


I have an application where I continuously have to run some async code in the background. I created a minimal simulation of my application.

let promise_chain = Promise.resolve();
let rejected_promise_count = 0;

const interval_id = setInterval(
    // Do some important polling. I will just always reject to demonstrate the problem.
    () => {
        promise_chain = promise_chain.then(() => {
            rejected_promise_count += 1;
            return Promise.reject();
        })
    },
    10
);

// Set timeout simulates the program being done.
setTimeout(
    () => {
        clearInterval(interval_id);
        promise_chain
            .then(() => end("Resolved! :D"))
            .catch(() => end("Rejected! D:"));
    },
    1000
);

function end(message) {
    console.log(message);
    console.log(`Amount of rejected promises created: `, rejected_promise_count);
}

This gives a long list of these:

(node:29217) UnhandledPromiseRejectionWarning: undefined
(node:29217) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). (rejection id: 2)
(node:29217) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.
(node:29217) PromiseRejectionHandledWarning: Promise rejection was handled asynchronously (rejection id: 2)
(node:29217) UnhandledPromiseRejectionWarning: undefined
(node:29217) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). (rejection id: 3)
(node:29217) PromiseRejectionHandledWarning: Promise rejection was handled asynchronously (rejection id: 3)
(node:29217) UnhandledPromiseRejectionWarning: undefined
(node:29217) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). (rejection id: 4)
(node:29217) PromiseRejectionHandledWarning: Promise rejection was handled asynchronously (rejection id: 4)

Eventually ended by these:

(node:29217) UnhandledPromiseRejectionWarning: undefined
(node:29217) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). (rejection id: 87)
(node:29217) PromiseRejectionHandledWarning: Promise rejection was handled asynchronously (rejection id: 87)
(node:29217) UnhandledPromiseRejectionWarning: undefined
(node:29217) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). (rejection id: 88)
Rejected! D:
Amount of rejected promises created:  1
(node:30920) PromiseRejectionHandledWarning: Promise rejection was handled asynchronously (rejection id: 89)

I'm not quite sure why node doesn't want me to handle these asynchronously, but given it runs on an interval, I really have no other choice.

How do I get rid of the endless list of warnings, and more importantly, how do I make sure in the future node will not terminate this process because it thinks I am not handling the rejections?


Solution

  • It depends what you want to do, the correct way to handle it is like this:

    const interval_id = setInterval(
        () => {
            promise_chain = promise_chain.then(() => {
                rejected_promise_count += 1;
                return Promise.reject();
            });
            //ignore error here, you catch it in the setTimeout
            promise_chain.catch(ignore=>ignore);
        },
        10
    );
    

    That will output:

    Rejected! D:
    Amount of rejected promises created:  1
    

    This because you reject the first time so the chain is broken and all other then are not executed.

    If you would like to continue executing and want to know how many passed and failed you can do something like this:

    //using actual results
    let results = [];
    //special Fail value to indicate rejected promise
    let Fail = function(reason){this.reason=reason;};
    let isFail = object=>(object&&object.constructor===Fail);
    let isNotFail = object=>!isFail(object);
    
    let promise_chain;
    let somePromise = ()=>Promise.reject(new Error("Some reason"));
    
    const interval_id = setInterval(
        () => {
            promise_chain = (promise_chain||somePromise())
            .then(
              result => {
                results.push(result);
                return somePromise();
              }
            )
            .catch(//catch the rejection and return a Fail type value
              error=>new Fail(error)
            );
    },
        10
    );
    
    // Set timeout simulates the program being done.
    setTimeout(
        () => {
            clearInterval(interval_id);
            promise_chain
            .then(
              result => {
                //add the last result to results
                results.push(result);
                console.log(
                  "rejected promises:",
                  results.filter(isFail).length
                  //you can get the errors like so:
                  //results.filter(isFail).map(fail=>fail.reason)
                );
                console.log(
                  "resolved promises:",
                  results.filter(isNotFail).length
                  //results.filter(isNotFail) is an array of resolved values
                )
              }
            );
                //this will never reject because we catch rejected promises
                //  and add fail types to results
                // .catch(() => end("Rejected! D:"));
        },
        1000
    );