【问题标题】:How can I catch an asynchronous error using JS promises?如何使用 JS 承诺捕获异步错误?
【发布时间】:2017-08-02 23:05:22
【问题描述】:

是否可以使用 ES6 .catch 的 Promise 语法来捕获异步错误?例如,以下内容不起作用(.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 块并 rejecting 捕获错误是唯一的解决方案吗?

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.

【问题讨论】:

  • assume the part of the code that is throwing the Error is in another named function - 这个(在你的代码中不存在)函数是否返回一个 Promise?
  • 是的,另一个函数正常返回一个promise,但是因为那个函数内部的一个异步函数抛出了一个错误,整个函数都抛出了一个错误。而且我知道第一个 sn-p 不能仅仅通过运行它来工作。期望的行为是“错误:”应该是 console.log'ed,但实际上错误是从链式调用中传播出来的:i.imgur.com/J6CyFW9.png
  • 不,catch 不会捕获超时内引发的错误,因为它位于不同的“线程”中。
  • 我想我似乎应该放弃对错误 throw 的渴望并拒绝它...?
  • 这可能是有用的阅读:stackoverflow.com/questions/33445415/…

标签: javascript asynchronous ecmascript-6 promise


【解决方案1】:

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

注意,一旦错误被处理,onFulfilled at chained .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);
});

【讨论】:

  • 我可以建议在承诺链的末尾使用.catch,而不是使用.then 的第二个参数吗?这样,它就会捕获 Promise 链中的任何错误。 .catch(e => { console.log("Error: " + e); })
  • 不确定您的意思?错误在.then(onFulfilled, onRejected).then(onFulfilled, onRejected)处处理,如果throw未在onRejected处使用链式.then()到达onFulfilled
  • 我的意思是这样的:jsfiddle.net/persianturtle/jhojwyqd 如果不觉得更好,请告诉我。我很困惑你为什么不这样做。例如,如果 Promise 链在 OP 的实际用例中很长,我想这会更安全。
  • @RaphaelRafatpanah “更好”是什么意思? jsfiddle 链接上的javascriptstackoverflow.com/questions/42755042/… 链接的jsfiddle 上的模式相同,是吗? OP 可以在onRejected.catch() 出现throw 错误,或者不是throw 错误链接.then(),如果有的话,应该以可能的链接.then() 到达onFulfilled。错误在onRejected.catch() 处理。 throw 是否处理错误取决于预期结果。
  • 更好更安全,因为它会捕获整个承诺链中的任何拒绝。我只是想知道您是否反对使用catch
【解决方案2】:

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

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

“是的,另一个函数正常返回一个 Promise,但是因为该函数内部的异步函数抛出了一个错误,所以整个函数都抛出了一个错误。” ——克里斯托弗·施罗巴

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


无论你的 Promise 中的错误有多深。这是一个示例函数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,它不能以这两种方式中的任何一种方式运行——即抛出错误但不返回承诺或将错误发送到延续的函数——你是处理一块垃圾,你编写的代码依赖于f 也将是垃圾。

【讨论】:

    【解决方案3】:

    没有办法像您的第一个示例那样捕获引发的错误。这里的问题是您使用的是 Explicit Promise Construction Antipattern。您正试图让 Promise 构造函数做的比它需要做的更多。

    相反,您应该承诺最少 数量的异步功能,并在此基础上进行构建。在这种情况下,这将涉及产生一个在解决之前等待一定时间的承诺。大多数第 3 方承诺库已经有一个 .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);
      });

    【讨论】:

      猜你喜欢
      • 2018-07-19
      • 2020-10-20
      • 2018-05-28
      • 1970-01-01
      • 2018-11-01
      • 2015-10-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多