humanityANDpeace
humanityANDpeace

Reputation: 4637

What determines that a promise rejection is unhandled in node.js (console/script)?

Javascript Promises if I had to oversimplify are to my understanding a "way to act upon stuff later, scheduled via the .then() method".

After doing the following in my terminal:

BASE$> node
> var promise = Promise.reject("reason 42");

I was therefore supprised to see this result:

> (node:8783) UnhandledPromiseRejectionWarning: reason 42
(node:8783) 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: 1)
(node:8783) [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.

because I was about to write.

> promise.catch(console.log);

The thing that motiviates this question is:
Can I be reasonably sure that node.js only pulls of this warnings (and threat "in the future I will bail completely", because of the code being run stepwise in the node.js console/REPL?

How has node.js come already to the conclusion that the promise rejection was to be unhandled?

I have hence tested the following to work differently

  1. Evaluation of "same code" combined in one REPL iteration:
    var promise = Promise.reject("reason 42"); promise.catch(console.log);
  2. Evaluation of "same code" from a file (e.g. tmp.js) with content
    var promise = Promise.reject("reason 42") 
    promise.catch(console.log);`)
    
    via node tmp.js

both yielding the expected output "reason 42" not presenting any warning as shown above earlier.

Hence how does this work out? Can my assumption be confirmed that the determination of an unhandled promise in the node console REPL, is reflection the reach end in each REPL loop iteration?

Upvotes: 1

Views: 423

Answers (2)

Thomas
Thomas

Reputation: 12637

I would like to extend on @estus answer, particularly the first sentence:

In order for promise rejection to be considered handled, it should be chained with catch or then with 2 arguments on same tick.

I'd not fully agree with that. I'd argument that every promise where then or catch has be called is fine, but when calling these methods you create new Promises and they have just inherited the problem.

We often talk about promise-chains, but actually it's a tree, as you can branch multiple "child-promises" from the very same "parent", and the error affects all of them.

const root = Promise.reject("reason 42");

const a = root.then(value => value*2)
              .then(...);

const b = root.then(value => value*3)
              .then(...);

So, you build a chain/tree of promises; an error occurs. The Error is propagated to the child-promises in that tree, ... If that (or any other) error reaches any leaf-promise (without being catched somewhere along the line) you'll get an UnhandledPromiseRejectionWarning.

There are a million things you can do with promises and how you chain/branch them and how/where you catch your errors, ... so the best summary I can give you on that:

Since Promises are all about time, you have time until the Error reaches the end of the chain to catch it.

Can I be reasonably sure that node.js only pulls of this warnings (and threat "in the future I will bail completely", because of the code being run stepwise in the node.js console

Yes

Upvotes: 2

Estus Flask
Estus Flask

Reputation: 222474

In order for promise rejection to be considered handled, it should be chained with catch or then with 2 arguments on same tick.

This will cause UnhandledPromiseRejectionWarning:

var promise = Promise.reject("reason 42");

setTimeout(() => {
  promise.catch(console.error);
});

This won't cause UnhandledPromiseRejectionWarning:

var promise = Promise.reject("reason 42");
promise.catch(console.error);

Asynchronously evaluated lines in Node.js REPL result in a delay between them. In order to evaluate lines synchronously in the order they were written, editor mode can be used. Or the code can be written to to be unambiguously evaluated as a block:

;{
var promise = Promise.reject("reason 42");
promise.catch(console.error);
}

Or IIFE:

(() => {
var promise = Promise.reject("reason 42");
promise.catch(console.error);
})()

Upvotes: 3

Related Questions