1

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?

humanityANDpeace
  • 4,350
  • 3
  • 37
  • 63
  • `I was therefore supprised to see this result because I was about to write` It can't read your mind - at that point, it had no idea that it was going to be handled. So, rather than wait some indeterminate amount of time and then check whether the rejection is still unhandled, it'll throw immediately. – CertainPerformance Oct 25 '18 at 10:01

2 Answers2

3

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);
})()
Estus Flask
  • 206,104
  • 70
  • 425
  • 565
  • so the rejections of **asyncrhonous** constructs (i.e. Promises), need to be **synchronously** handled and within `REPL` two iterations do not qualifu as **synchrounous**, do I understand this correctly? – humanityANDpeace Oct 25 '18 at 10:39
  • This requirement `catch()` the rejection of the promise **synchronously** is implied in the phrasing "..*on same tick*." of your answer's first sentence of this answer, how very concise +1 :) – humanityANDpeace Oct 25 '18 at 11:44
2

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

Thomas
  • 11,958
  • 1
  • 14
  • 23
  • Well done, this of course is another pitfall. Though important, however the real confusion and core issue of my question was to have the confirmation that a) the `catch()` of a rejection is to be done synchronously to avoid the warning and b) each `REPL` iteration of node.js console is not synchronous anymore with the previous one. +1 as it however highlight another pitfall. – humanityANDpeace Oct 25 '18 at 11:46