correct way to handle errors inside a Promise

2019-02-19 05:57发布

问题:

Currently, I'm trying to decide what pattern I should use while dealing with errors inside a Promise. For instance, I have the code below

promiseFunc()
.then(result => {

    console.info(`.THEN:: ${result}`)
})
.catch(error => {

    console.info(`.CATCH:: ${error}`)
})

function promiseFunc() {

    return new Promise((resolve, reject) => {

        setTimeout(() => {

            throw Error("setTimeout's callback error")
            resolve('resolution')           
        }, 1000)
    })
}

What I can't get is what approach should be used to reject the Promise if a function inside it (setTimeout(), in my case) throws an Error. In other words, I need a rejection instead of an error, but the only idea that comes to my mind is to add a try/catch block and reject the Promise from the catch.

回答1:

What approach should be used to reject the Promise if a function inside it (setTimeout(), in my case) throws an Error

An asynchronous callback must never throw an exception. Your function that you try to promisify (setTimeout) either throws a synchronous exception (which new Promise handles), or it calls the callback. In the callback you must call resolve or reject, and do so without throwing an exception.

If you want to do additional things in the callback (besides calling resolve/reject), things that could throw an exception: don't!

The new Promise should wrap only the immediate function that you want to promisify, nothing else. Do more things in then callbacks that are chained to the promise - then will handle exceptions in its callback just fine:

function promiseFunc() {
  return new Promise(resolve => {
    setTimeout(resolve, 1000);
//             ^^^^^^^ nothing can go wrong in here
  }).then(() => {
    throw "setTimeout's callback error";
//  ^^^^^ here, it will lead to a rejection
    return "resolution";
  });
}


回答2:

You are throwing an error in an asynchronous function, instead of rejecting the promise.

Change throw Error("") to reject(""):

promiseFunc()
  .then(result => {
    console.info(`.THEN:: ${result}`)
  })
  .catch(error => {
    console.info(`.CATCH:: ${error}`)
  })

function promiseFunc() {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      reject("setTimeout's callback error")
      resolve('resolution')
    }, 1000)
  })
}

Or add a try-catch statement, and reject it in the catch block

setTimeout(() => {
  try {
    throw new Error("setTimeout's callback error")
  } catch(error) {
    reject(error)
  }
  resolve('resolution')
}, 1000)


回答3:

Resolve, Reject and Error are three distinct things and your code need to handle cases when you need to resolve and when you need to reject it. If the condition you want is full-filled then you call the resolve method, with the condition can't be full-filled then call the reject() method.

In case of any errors thrown by your code or any other reason the single catch() block in the end of chain would be executed.

// doAsyncOperation1() returns a promise.
doAsyncOperation1()
.then(() => {
  // ...
  // doAnotherAsyncOperation() returns a promise
  // which will be inserted into the chain.
  return doAsyncOperation2();
})
.then((output) => {
  // output will be the value resolved by the
  // promise which was returned from doAsyncOperation2()
  // ...
  return doAsyncOperation3();
})
.catch((err) => {
  // Handle any error that occurred in any of the previous
  // promises in the chain.
});