5

I know that stackoverflow is full of similar question and I've read a lot of them.

From what I got a throw inside a promise should reject it, as I can read in the documentation:

If the executor throws an exception, its value will be passed to the reject resolving function.

But even after read a lot of post about promises and throw I still don't understand the snippet of code I'm pasting and why it happens.

function foo(a, b, cb) {
  setTimeout(() => {
    cb('Inner error *!?"$%&#@"');
  }, 0);
}

const getThePromise = () => {
  return new Promise((resolve, reject) => {
    const cb = (err) => {

      /* >>> ************ */

      throw err;       // catch not called
      // reject(err);  // catch called

      /* ************ <<< */

    }
    foo('foo', 'dudee', cb);
  });
}

getThePromise()
.catch((err) => {
  console.log('CATCH:', err);
})
.then((res) => {
  console.log('then...');
})

I don't understand why if I use the throw the .catch of the promise is not called but if I use the reject it is called.

Just for sake of clarification I'm using Node.js v6.2.2 in a Mac OS/X 10.11 but I don't think it could be also a browser issue.

nkint
  • 11,513
  • 31
  • 103
  • 174
  • Put `.error((err) => { console.log('ERROR:', err); })` too there – Shaharyar Aug 31 '16 at 09:21
  • @Shaharyar, native Promise.prototype doesn't have `error` [function](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Promise#Promise_prototype) – G07cha Aug 31 '16 at 09:27
  • 2
    `throw` rejects promise if it's called synchronously only. remove your `setTimeout` and it will work. In async case use `reject`. – Maxx Aug 31 '16 at 09:30
  • "If the executor throws an exception" - not your case. Your executor doesn't throw. Try `new Promise(() => {throw new Error('test')}).catch( e => console.log(e))` – Yury Tarabanko Aug 31 '16 at 09:31
  • 1
    Why do you want to Throw the error? That is the whole point of the `reject` method. – Nicholas Robinson Aug 31 '16 at 09:32

1 Answers1

8

You are throwing your error inside an asynchronous setTimeout call, which will lead to an uncaught error. The asynchronous code will not execute in the same context as the try-catch block. This has nothing to do with the promise API. This is just part of the behavior of asynchronous code execution in JavaScript.

Take a look at the following example.

const asyncOperation = err => {
  try {
    setTimeout(function() {
      throw err; // will be dropped onto the event queue
      // until the call stack is empty
      // even if this takes longer than
      // a second.
    }, 1000);
  } catch (e) {
    console.log(e) // will not be called
  }
}

asyncOperation('Inner error *!?"$%&#@"')

And now the same example with the try-catch block inside the setTimeout call and the error being thrown inside the try block.

const asyncOperation = err => {
  setTimeout(function() {
    try {
      throw err // here the error will be throw inside
    } catch (e) { // the try block and has the same execution 
      console.log(e) // context.
    }
  }, 1000);
}

asyncOperation('Inner error *!?"$%&#@"')

You can find more information regarding the Promise.catch right here.

Promise.prototype.catch()

The catch() method returns a Promise and deals with rejected cases only.

There is actually an example with the same situation you are describing in your example. Check out

Gotchas when throwing errors

// Errors thrown inside asynchronous functions will act like uncaught errors
var p2 = new Promise(function(resolve, reject) {
  setTimeout(function() {
    throw 'Uncaught Exception!';
  }, 1000);
});

p2.catch(function(e) {
  console.log(e); // This is never called
});
DavidDomain
  • 14,976
  • 4
  • 42
  • 50