4

why the code below can not catch the error ? the 'catch' do not work and the error throwout and quit. I run the code on nodeJs 8.9

new Promise(function(resolve,reject){
    console.log('construct a promise...')
    setTimeout(() => {
        throw new Error('async operation failure!')
    },1000)
})
.then(() => {
    //never happen
    console.log('happen?wrong!!!')
})
.catch(e => {
    console.warn('execute promise failure! catch it')
})

if remove the setTimeout, the 'catch' works

new Promise(function(resolve,reject){
    console.log('construct a promise...')
    throw new Error('async operation failure!')
})
.then(() => {
    //never happen
    console.log('happen?wrong!!!')
})
.catch(e => {
    console.warn('execute promise failure! catch it')
})

why this happen? please help , thanks!

Dean Chen
  • 91
  • 3
  • 5

2 Answers2

8

To understand why you cannot just throw an error from within the callback of setTimeout you need to understand a few concepts.

The Stack

As a program is running information is stored in a data structure called the stack. When a method is called information is stored on the stack until the method returns. Since methods generally contain calls to other methods the stack grows and shrinks until all the methods have returned (this is generally the end of the program). When an error is thrown it is passed down the stack of function calls that have yet to return until it is caught and handled, or reaches the "main" method, which generally causes the program to crash.

The Event Loop

The event loop is what powers the asynchronous functionality in Javascript. When setTimeout or any other asynchronous method is called the callback is placed in a queue called the event loop. As the javascript runtime executes the program and reaches the end of the stack (e.g. all methods have returned) instead of merely exiting the program it first looks to see if there is anything in the event loop, if there is it begins executing it causing the stack to grow and shrink once more. When there is nothing left in the event loop, and the stack is empty then the program is free to exit.


This means that when you call setTimeout the callback that is passed into the method ends up being executed in a different stack frame. This means that it is impossible for the error to be thrown down the stack and be caught by the promise, because the stack the promise was created in has already finished executing and is no more.

I would suggest watching this video on the topic, it goes into great detail and helps you visualize what is going on: What the heck is the event loop anyway?

Jake Holzinger
  • 5,783
  • 2
  • 19
  • 33
5

setTimeout() has it's own async callback. Exceptions in that callback only go back into the timer event system, they don't go anywhere else so the promise can't catch them. You should call reject(...) from inside the setTimeout().

new Promise(function(resolve,reject){
    console.log('construct a promise...')
    setTimeout(() => {
        reject(new Error('async operation failure!'));
    },1000)
})
jfriend00
  • 683,504
  • 96
  • 985
  • 979