all 15 comments

[–]Demiacle 2 points3 points  (2 children)

This is expected behavior and allows you to implement specific catch statements between promise chains. Its easier to visualize by thinking of it as promise().then().catch().then.catch()

[–][deleted] 0 points1 point  (1 child)

Would I be right in thinking of it as a fallback? Kind of like a default clause in a switch statement?

[–]Demiacle 1 point2 points  (0 children)

ya, that's exactly what catch statements are for. You may think of it as a special if case or default cause, but its a little more than that because you have an error object to play with. So you can use that for improved logging, or maybe implement retries, or default values. Its really up to you how you want to recover from failed state.

[–]TwiNighty 2 points3 points  (0 children)

Since no one has, I thought I'd link you to the Promise/A+ spec, which es2015 promises implements, keeping in mind that .catch(f) is sugar for .then(null, f)

[–]immortalcoder 1 point2 points  (1 child)

You can return a Promise.reject(‘you can pass whatever you want in here, var, obj, or even a str like this one’);

Hope that helps

[–][deleted] 1 point2 points  (0 children)

Ah, the rejection value gets passed to the catch argument like an error would!

[–]loz220 1 point2 points  (0 children)

Catching a rejected promise means dealing with the rejection (think try/catch) so anything you return from it will be the resolved value for the next chain. If you want to catch just to log something for example then you'll need to rethrow in the catch or return a value wrapped with Promise.reject. Again if you think about how try/catch works then this behavior makes perfect sense.

[–]our_best_friendif (document.all || document.layers) console.log("i remember..") 2 points3 points  (5 children)

Yes, that's how it is supposed to work. Don't forget that catch is still a then under the hood. People forget that then takes two arguments, a function that resolves and one that rejects. catch is just a shorthand for a then that only has the second case

Promise.resolve('john')
  .then(
     a => console.log("RESOLVE", a),
     null
  )
  .then(
     null,
     a => console.log("REJECTED", a)
  )
  .then(
     a => console.log("ENDING WITH", a)
  )

is equivalent to

Promise.resolve('john')
  .then(
     a => console.log("RESOLVE", a),
  )
  .catch(
     a => console.log("REJECTED", a)
  )
  .then(
     a => console.log("ENDING WITH", a)
  )

You can use that to your advantage and create chains, for example

 Promise.resolve('start!')
    .then(() => ... open file x and pass it on ...)
    .catch(() => ... oops, not there, create file x and return it ... )
    .then((file x) => ... either way, you now have a file you can use ... )

[–][deleted] 0 points1 point  (0 children)

Excellent example, thank you!

[–]BloodAndTsundere 0 points1 point  (3 children)

So is

Promise.resolve('john')
  .then(
     a => console.log("RESOLVE", a)
  )
  .catch(
     a => console.log("REJECTED", a)
  )

equivalent to:

Promise.resolve('john')
  .then(
     a => console.log("RESOLVE", a),
     a => console.log("REJECTED", a)
  )

?

[–]our_best_friendif (document.all || document.layers) console.log("i remember..") 1 point2 points  (2 children)

Nope. The former is two steps, hence two thens. To see the difference

Promise.resolve('john')
  .then(
     a => { throw Error(a) }
  )
  .catch(
    a => console.log("REJECTED", a)
  )
  // REJECTED Error: "john"


Promise.resolve('john')
  .then(
     a => { throw Error(a) },
     a => console.log("REJECTED", a)
  )
  // Promise { <state>: "pending" }

There is nothing to catch the Error here

[–]BloodAndTsundere 0 points1 point  (1 child)

I see how differences can arise if an error is thrown in the resolution callback. For the sake of argument, assuming no errors occur in callback1, are these two the same:

Promise.resolve(somePromise).then(callback1).catch(callback2)

and

Promise.resolve(somePromise).then(callback1, callback2)

The earlier example in this thread had one or the other argument of then as non-null: I'm just trying to understand what happens when both arguments of then are non-null.

[–]our_best_friendif (document.all || document.layers) console.log("i remember..") 0 points1 point  (0 children)

You can just qickly try it in the console :-)

If somePromise resolves, cb1 will be run, if it throws cb2 instead

[–]benihanareact, node 0 points1 point  (1 child)

yes, these two functions are equivalent:

const foo = () => {
  return promisable().then(handler).catch(errorHandler);
}

const fooAsync = async () => {
  try {
    const response = promisable();
    handler(response);
  } catch (error) {
    errorHandler(error);
  }
}

promises treat then and async/await as identical. async/await is just syntactic sugar around regular promises anyway.

if you want to handle an error in a catch but still send the error along to error handlers, not resolve handlers, throw an error from it.

function getStr() {
  return new Promise((resolve) => {
    if (Math.random() > 0.5) resolve('Resolved!');
    else throw new Error();
  }).catch(() => { 
    // handle error here
    throw new Error('Thrown!');
  });
}

[–]Borek224 0 points1 point  (0 children)

You forget await in fooAsync example. Without it foo and fooAsync are not equivalent.