如何使用JS promises捕获异步错误?

时间:2017-03-13 00:48:53

标签: javascript asynchronous ecmascript-6 promise

是否可以使用承诺的ES6 .catch语法捕获异步错误?例如,以下操作无效(.catch没有捕获错误):

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

但是这个同步版本确实:

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

使用try / catch块并reject捕获错误的唯一解决方案是执行以下操作吗?

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);
});

为了这个问题,假设抛出错误的代码部分在另一个命名函数中,因此它无法访问reject函数。

谢谢!

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

3 个答案:

答案 0 :(得分:2)

resolve()构造函数中使用reject()Promise。处理onRejected.catch()处的错误。

注意,一旦处理错误,应该联系onFulfilled链接.then(),如果有的话,除非在throwonRejected中使用.catch() ,将错误明确传递给链式.then(_, onRejected).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);
});

答案 1 :(得分:1)

没有办法像第一个例子那样捕获错误。这里的问题是您使用的是Explicit Promise Construction Antipattern。您正试图让Promise构造函数执行的操作超出了它的需要。

相反,您应该宣传最小数量的异步功能,并在此基础上构建。在这种情况下,这将涉及产生在解决之前等待一定时间的承诺。大多数第三方承诺库已经有.delay()方法,但创建自己的方法非常容易:

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

然后你可以在此基础上构建,并轻松捕获错误:



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);
  });




答案 2 :(得分:0)

  

“为了这个问题,假设抛出错误的代码部分在另一个命名函数中,因此它无法访问拒绝函数。” - Christopher Shroba

     

“这个(你的代码中不存在)函数会返回一个Promise吗?” - Jaromanda X

     

“是的,另一个函数通常会返回一个承诺,但是因为该函数中的异步函数抛出错误,整个函数都会抛出一个错误。” - Christopher Shroba

下次发布您的代码时,因为您用英语描述问题的能力永远不会像实际代码那样好。 “异步函数”是指一个返回promise的函数吗?如果是的话......

错误在你的Promises中的深度并不重要。这是一个示例函数three,它调用一个函数two,它调用一个函数one,它有可能在JSON形成不良的情况下抛出一个错误。每一步都为最终计算做出了有价值的贡献,但是如果one抛出错误,它将在整个Promise链中冒出来。

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

否则通过“异步函数”你的意思是它是一个不返回Promise并且可能使用延续的函数?在这种情况下,我们将修改one以将异步函数包装在promise中,然后twothree将起作用。重要的是,我在任何Promise函数中都使用try / catch

// 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
  

哦,如果你有一个函数f,它在这两种方式中都不起作用 - 即抛出错误但不返回promise或将错误发送到continuation的函数 - 你“处理一块垃圾,而你编写的代码依赖于f也将是垃圾。