12

So I been reading a tutorial about Javascript promises these days.

here is an example from it used to explain the macrotask queue(i.e. the event loop) and the microtask queue.

let promise = Promise.reject(new Error("Promise Failed!"));
promise.catch(err => alert('caught'));

// no error, all quiet
window.addEventListener('unhandledrejection', event => alert(event.reason));

It says that because promise.catch catches the error so the last line, the event handler never gets to run. I can understand this. But then he tweaked this example a little bit.

let promise = Promise.reject(new Error("Promise Failed!"));
setTimeout(() => promise.catch(err => alert('caught')));

// Error: Promise Failed!
window.addEventListener('unhandledrejection', event => alert(event.reason));

This time he says the event handler is gonna run first and catch the error and after this the promise.catch catches the error eventually.

What I do not understand about the second example is, why did the event handler run before the promise.catch?

My understanding is,

  1. Line one, we first encounter a promise, and we put it on the microtask queue.
  2. Line two, we have a setTimeout, we put it on the macrotask queue,
  3. Line three, we have an event handler, and we put the handler on the macrotask queue waiting to be fired

Then, because microtask has higher priority than macrotask. We run the promise first. After it, we dequeue the first task on macrotask queue, which is the setTimeout. So from my understanding, the error should be caught by the function inside setTimeout.

Please correct me.

Joji
  • 4,703
  • 7
  • 41
  • 86
  • Have a look at https://www.youtube.com/watch?v=8aGhZQkoFbQ This presentation about the event loop helped me a lot! – fwoelffel Jun 04 '19 at 14:36

1 Answers1

3

You are wrong about step 3). The handler will be added synchronously. Then the microtask queue gets run, and the promise rejects. As no .catch handler was added yet, an unhandled rejection gets thrown.

And I think you are mixing between when a callback gets added and when a callback gets executed. Consider this case:

  (new Promise).then(function callback() { });

The callback will be added synchronously, but it will never be called as the promise never resolves. In this case:

  Promise.resolve().then(function callback() { });

the callback again gets added synchronously, but the promise resolution happens in a microtask, so the callback will be executed a tick later.

Jonas Wilms
  • 132,000
  • 20
  • 149
  • 151
  • If as you said step 3). The handler is added synchronously, then why in my first example, the handler is never triggered? Given that `promise.catch(err => alert('caught'));` is in the microtask queue and should have lower priority than regular code. So it should be fired before `promise.catch` is run. – Joji Jun 01 '19 at 20:38
  • No, `promise.catch(...)` also runs synchronously in your first example. – Jonas Wilms Jun 01 '19 at 23:36
  • shouldn't `promise.then(...)` pass through the microtask queue, and runs after the current code? so I thought `promise.catch(...)` would be the same – Joji Jun 02 '19 at 03:19
  • You are mixing when a callback was added and when it gets executed. Have a look at the edit, I hope it helps clearing up things – Jonas Wilms Jun 02 '19 at 14:36
  • Hi thank you for your answer! but I still have trouble understanding the first example from my original question. so as you said" `Promise.resolve().then(function callback() { });` the promise resolution happens in a microtask, so the callback will be executed a tick later." then I think the same goes for `promise.catch(err => alert('caught'));`. the catch will happed after all current synchronous code gets executed. Then in that `case window.addEventListener('unhandledrejection', event => alert(event.reason));` should catch the error instead. (I'm talking about the first example) – Joji Jun 03 '19 at 20:49
  • @Joji I feel like you might be forgetting that the current code will execute before polling any more tasks. For instance if I put `while(true);` at the bottom of your code I would never see an alert at all (also your code/page would break but still). Events arent polled after each statement. – ug_ Jun 03 '19 at 23:03
  • @ug_ not sure exactly what you mean by `polling`. did you mean that even tho ` window.addEventListener('unhandledrejection', event => alert(event.reason));` was added synchronously but the event wouldn't get fired until all the current code get executed, including `promise.catch` from the microtask? – Joji Jun 04 '19 at 01:01
  • @Joji Correct. By polling I meant grabbing more tasks to execute. – ug_ Jun 04 '19 at 01:29