0

Diving into Promises, I found something that confused me for a second. However, I realized what’s happening and decided to share it here for others who might bump into it.


I have the following JavaScript:

new Promise(function (resolve, reject) {
    foo();

    setTimeout(function () {
        resolve();
    }, 400);
}).catch(function (err) {
    console.log('Caught it:', err.message);
});

When I run it, I get:

Caught it: foo is not defined

Which is the expected result. However, if I try to call foo() in the setTimeout callback like this:

new Promise(function (resolve, reject) {
    setTimeout(function () {
        foo();
        resolve();
    }, 400);
}).catch(function (err) {
    console.log('Caught it:', err.message);
});

I get:

ReferenceError: foo is not defined
    at Timeout._onTimeout (C:\Users\Hristiyan\Desktop\promise.js:3:13)
    at tryOnTimeout (timers.js:224:11)
    at Timer.listOnTimeout (timers.js:198:5)

Question: Why don’t I get the same result? Why doesn’t the handler defined with catch() handle the error? I mean, the error occurs under the same code block?

dodov
  • 5,206
  • 3
  • 34
  • 65
  • 1
    "*The error occurs under the same code block?*" - no, the callback passed to `new Promise` and the callback passed to `setTimout` are two different functions – Bergi May 05 '18 at 14:59
  • Agreed. I meant that it is _visually_ in the same code block, which misled me. Soon after, I remembered that "visually" doesn’t mean anything. – dodov May 05 '18 at 15:26

1 Answers1

0

The reason this happens is because the error occurs in a separate call stack where there’s nothing to catch it.

In the first example, the error occurs in the promise resolver, which is a function provided by the Promise. Then, the Promise can catch the error. It would look something like this if each number represents one level of the stack deeper:

  1. [main]
  2. Promise (catches)
  3. resolver (throws)

Resolver throws the error, it travels up the stack, and is caught by the Promise.


In the second example, the error occurs some time later, in this case - after 400ms, when the call stack of the Promise is long gone and finished its execution with no errors. Something like:

  1. [main]
  2. Promise
  3. resolver

*400ms later*

  1. setTimeout handler (throws) (and nothing above to catch)

So the error occurs on another call stack with nothing above it to catch. Even though it’s under the Promise’s code block, it’s not in its call stack. So to make the code work as expected, a catch block is needed to catch the error. When caught, we inform the Promise by invoking its reject handler:

new Promise(function(resolve, reject) {
  setTimeout(function() {
    try {
      foo();
    } catch (e) {
      return reject(e);
    }

    resolve();
  }, 400);
}).catch(function(err) {
  console.log('Caught it:', err.message);
});
dodov
  • 5,206
  • 3
  • 34
  • 65