How can I catch an asynchronous error using JS pro

2019-05-16 11:43发布

Is it possible to catch asynchronous errors using the ES6 .catch syntax of promises? For example, the following doesn't work (the .catch doesn't catch the error):

new Promise((resolve, reject)=>{
    setTimeout(()=>{throw new Error("uh oh")}, 1);
}).then(number=>{
    console.log("Number: " + number);
}).catch(e=>{
    console.log("Error: " + e);
});

But this synchronous version does:

new Promise((resolve, reject)=>{
    throw new Error("uh oh");
}).then(number=>{
    console.log("Number: " + number);
}).catch(e=>{
    console.log("Error: " + e);
});

Is the only solution to do something like the following, using a try/catch block and rejecting the error in the catch?

new Promise((resolve, reject)=>{
    try {
        setTimeout(()=>{throw new Error("uh oh")}, 1);
    }
    catch(e) {
        reject(e);
    }
}).then(number=>{
    console.log("Number: " + number);
}).catch(e=>{
    console.log("Error: " + e);
});

For the sake of this question, assume the part of the code that is throwing the Error is in another named function, so it doesn't have access to the reject function.

Thanks!!

Edit: Here is a more complete example of what I'd like to do, in JSFiddle.

3条回答
我欲成王,谁敢阻挡
2楼-- · 2019-05-16 12:05

There isn't a way to catch an error thrown like your first example. The problem here is that you are using the Explicit Promise Construction Antipattern. You are trying to have the Promise constructor do more than it needs to do.

Instead, you should promisify the smallest amount of asynchronous functionality and build on top of that. In this case, that would involve producing a promise that waits for a certain amount of time before resolving. Most 3rd party promise libraries already have a .delay() method, but it's very easy to create your own:

let delay = duration => new Promise(resolve => setTimeout(resolve, duration));

Then you can build on top of that, and catch the error easily:

let delay = duration => new Promise(resolve => setTimeout(resolve, duration));

delay(1)
  .then(() => {
    throw new Error("uh oh");
  })
  .then(number => {
    console.log("Number: " + number);
  }).catch(e => {
    console.log("Error: " + e);
  });

查看更多
爷的心禁止访问
3楼-- · 2019-05-16 12:05

"For the sake of this question, assume the part of the code that is throwing the Error is in another named function, so it doesn't have access to the reject function." – Christopher Shroba

"does this (non-existent in your code) function return a Promise?" – Jaromanda X

"Yes, the other function returns a promise normally, but because an asynchronous function inside that function is throwing an Error, the entire function is throwing an Error." – Christopher Shroba

Well next time post your code, because your ability to describe the problem with English will never be as good as actual code. By "asynchronous function" do you mean a function that returns a promise? If so ...


It doesn't matter how deep the errors throw in your Promises. Here's an example function three which calls a function two which calls a function one which has potential to throw an Error in the event the JSON is formed poorly. Each step makes a valuable contribution to the final computation, but in the event one throw an error, it will bubble up through the entire chain of Promises.

const one = (json) => new Promise((resolve, reject) => {
  resolve(JSON.parse(json))
})

const two = (json) => one(json).then(data => data.hello)

const three = (json) => two(json).then(hello => hello.toUpperCase())

three('{"hello":"world"}').then(console.log, console.error)
// "WORLD"

three('bad json').then(console.log, console.error)
// Error: unexpected token b in JSON at position 0


Otherwise by "asynchronous function" you mean it's a function that does not return a Promise and maybe uses a continuation instead? In which case, we'll modify one to wrap the async function in a promise, then two and three will work the same. Of importance, I did not use try/catch in any of my Promise functions

// continuation passing style async function
const asyncParse = (json, k) => {
  try {
    k(null, JSON.parse(json))
  }
  catch (err) {
    k(err)
  }
}

// one now wraps asyncParse in a promise
const one = (json) => new Promise((resolve, reject) => {
  asyncParse(json, (err, data) => {
    if (err)
      reject(err)
    else
      resolve(data)
  })
})

// everything below stays the same
const two = (json) => one(json).then(data => data.hello)

const three = (json) => two(json).then(hello => hello.toUpperCase())

three('{"hello":"world"}').then(console.log, console.error)
// "WORLD"

three('bad json').then(console.log, console.error)
// Error: unexpected token b in JSON at position 0
  


Oh, and if you have an a function f which does not function in either of these two ways – ie function that throws an error but doesn't return a promise or send the error to the continuation - you're dealing with a piece of rubbish and the code you write to depend on f will be rubbish too.

查看更多
Bombasti
4楼-- · 2019-05-16 12:17

Use resolve(), reject() within Promise constructor. Handle error at either onRejected or .catch().

Note, once error is handled, onFulfilled at chained .then(), if any, should be reached, unless throw is used within onRejected or .catch(), to explicitly pass error to chained .then(_, onRejected) or .catch()

function fn() {
  throw new Error("uh oh")
}

new Promise((resolve, reject) => {
  setTimeout(() => {
    try {
      resolve(fn())
    } catch (e) {
      reject(e)
    }
  }, 1);
}).then(number => {
  console.log("Number: " + number);
}, e => {
  console.log("Error: " + e);
});

查看更多
登录 后发表回答