1

I am reading about error handling, and the book says

If a throw occurs inside a Promises then handler function, then it is a rejection. One way to think about exceptions and rejections is that exceptions are synchronous errors and rejections are asynchronous errors.

Questions

  • The book writes rejection, but shouldn't that be unfulfilled?
  • I were under the impression that a throw is always an exception?
  • And why are exceptions only for synchronous code?
Sandra Schlichting
  • 25,050
  • 33
  • 110
  • 162
  • 2
    What book is that? And notice it says "*One way to think about …*", ie it's a (useful) simplification and not the ultimate truth with all technical details. – Bergi Sep 13 '21 at 23:35
  • 1
    @Bergi Linux Foundations course "NODE.JS APPLICATION DEVELOPMENT (LFW211)". The book feels like it is copy/paste from many different sources, and not really polished in any way. – Sandra Schlichting Sep 13 '21 at 23:36
  • 2
    "*it is a rejection*" - I wouldn't say that. The thrown value is still an exception in the `then` handler function. But it will *lead to* a rejection of the promise returned by the `then()` call. – Bergi Sep 13 '21 at 23:37
  • 1
    And once you `await` a rejected promise, you get an exception again… So the terminology is a bit fluent there. – Bergi Sep 13 '21 at 23:45
  • related: [Why are exceptions used for rejecting promises in JS?](https://stackoverflow.com/q/21616432/1048572) – Bergi Sep 14 '21 at 01:00

2 Answers2

4

An unfulfilled Promise is simply one that hasn't been fulfilled, which is quite possible even if it doesn't reject. For example:

const prom = new Promise((resolve) => {
  // code here that never calls `resolve`
});

That is a Promise that will never be fulfilled - but it won't reject either.

An "unfulfilled" Promise may not necessarily hang forever like the above - it's simply a Promise that hasn't resolved yet.

a throw is always an exception?

To an extent, yes, though it behaves somewhat differently when inside a Promise - it won't cause an error event. Instead, it'll try to find a .catch handler in the Promise chain it's currently in, and if it doesn't find one, an unhandledrejection event will be fired.

And why are exceptions only for synchronous code?

That's just how the language was designed.

CertainPerformance
  • 356,069
  • 52
  • 309
  • 320
  • I found this post that also explains the terminology. https://stackoverflow.com/a/29269515/256439 . I am not sure I understand what a `throw` then is. What is the difference of throwing and returning? – Sandra Schlichting Sep 13 '21 at 23:46
  • 1
    Throwing inside a Promise results in the Promise rejecting with the value that was thrown. Returning inside a Promise results in the Promise settling with the value returned - even if what was returned is an error. (eg `return new Error()` is possible - it'll go into the `.then` of a Promise chain, not a `.catch`) – CertainPerformance Sep 13 '21 at 23:49
1

I think you need to understand the reason why rejections are needed, the problem it tries to solve. Consider a simple example such as this:

try {
  iDoNotExist
} catch(e) {
  //e ReferenceError
}

Pretty simple, the code is being evaluated and "tried" for errors. There's such an error and well, it gets caught.

Now, what if we changed it a little bit into this:

try {
  setTimeout(function(){ iDoNotExist }, 1000);
} catch(e) {
  //e ReferenceError
}

If you run this code, you'll notice that your javascript environment will emit an Error. It's not being caught here. Why? Because the code that throws the error is not being run when it's being "tried", only the setTimeout function which simply schedules the function and well, that function is doing it's job, it's scheduling it correctly. This in turn is the same reason why so many people get it wrong when they try accessing a value that's product of an async function. E.g.

let result;
setTimeout(() => result = true, 1000);
console.log(result) // Why is this undefined? - Famous last question

Enter promises to the rescue, which not only allow you to recover a value which you would typically return, it also lets you recover any potential error that happens on said async operations. Providing you with semantics that are similar with synchronous code (even more when paired with async functions).

So, I would say rejections are complementary to exceptions. To preserve your program flow when there's an asynchronous boundary (just like fulfilled promises do to preserve function composition).

As for what you would call unfulfilled well... there's a couple of things. unfulfilled means "not fulfilled". A rejected promise IS, technically, an unfulfilled promise. However, an unresolved promise (a promise in a pending state) is ALSO an unfulfilled promise. Technically.

Most people will think of the latter but really it could be either. Mostly because it comes from a time where "fulfill" and "resolve" where used interchangeably (which is wrong). But also because that's typically the expected result of a promise resolution.

Under no circumstance is an unresolved promise a rejected promise. An unresolved promise is in a transitory state in which it could transit into either a fulfilled promise or a rejected one.

MinusFour
  • 13,913
  • 3
  • 30
  • 39