为什么“.catch(err => console.error(err))”气馁?

时间:2018-06-17 12:03:50

标签: javascript node.js promise try-catch

我正在使用promises并且代码如下所示:

function getStuff() { 
  return fetchStuff().then(stuff => 
    process(stuff)
  ).catch(err => {
    console.error(err);
  });
}

或者:

async function getStuff() { 
  try {
    const stuff = await fetchStuff();
    return process(stuff);
  } catch (err) { 
    console.error(err);
  }
}

我这样做是为了避免错过错误,但是被其他用户告知我不应该这样做而且不满意。

  • return ….catch(err => console.error(err))出了什么问题?
  • 我见过很多这样做的代码,为什么?
  • 我该怎么办?

5 个答案:

答案 0 :(得分:53)

为什么旧代码会这样做?

历史上,年龄较大(2013年之前)承诺图书馆“吞噬”未经处理的承诺拒绝,而您自己并未处理。从那以后的任何事情都不是这种情况。

今天会发生什么?

浏览器和Node.js已经自动记录未被捕获的承诺拒绝或具有处理它们的行为,并将自动记录它们。

此外 - 通过添加.catch,您正在向调用返回undefined的函数的方法发信号:

// undefined if there was an error
getStuff().then(stuff => console.log(stuff)); 

编写异步代码时应该问自己的问题通常是“代码的同步版本会做什么?”:

function calculate() { 
  try {
    const stuff = generateStuff();
    return process(stuff);
  } catch (err) { 
    console.error(err);
    // now it's clear that this function is 'swallowing' the error.
  }
}

我不认为如果发生错误,消费者会希望此函数返回undefined

总而言之 - 它是不受欢迎的,因为它让应用程序流程中的开发人员感到惊讶,浏览器无论如何都会记录未被捕获的承诺错误。

该怎么做:

没什么。这就是它的美丽 - 如果你写的:

async function getStuff() { 
  const stuff = await fetchStuff();
  return process(stuff);
}
// or without async/await
const getStuff = fetchStuff().then(process);

首先,无论如何 :

,你会得到更好的错误

如果我运行旧版本的Node.js怎么办?

旧版本的Node.js可能不会记录错误或显示弃用警告。在这些版本中,您可以使用console.error(或正确的日志记录工具)全局

// or throw to stop on errors
process.on('unhandledRejection', e => console.error(e));

答案 1 :(得分:13)

  

return ….catch(err => console.error(err))出了什么问题?

在您处理错误后,它会返回一个与undefined一致的承诺。

就它本身而言,在承诺链的末尾捕捉错误并记录它们很好:

function main() {
    const element = document.getElementById("output");
    getStuff().then(result => {
        element.textContent = result;
    }, error => {
        element.textContent = "Sorry";
        element.classList.add("error");
        console.error(error);
    });
    element.textContent = "Fetching…";
}

但是,如果getStuff()确实捕获错误本身以记录它并且不执行任何其他操作来处理它,就像提供合理的回退结果一样,则会导致undefined显示在页面中而不是“抱歉”。

  

我看过很多代码都是这样做的,为什么?

从历史上看,人们害怕承诺错误无处处理导致他们完全消失 - 被承诺“吞噬”。因此,他们在每个函数中添加了.catch(console.error),以确保他们在控制台中发现错误。

这不再是必要的,因为所有现代承诺实施都可以检测未处理的承诺拒绝,并将在控制台上发出警告。

当然,仍然需要(或者至少是良好的做法,即使你不希望有任何失败)在承诺链的末尾捕获错误当你不进一步返回承诺)。

  

我该怎么办?

对于呼叫者 return承诺的功能,请不要记录错误并通过执行此操作来吞下它们。只需返回promise,以便调用者可以捕获拒绝并适当地处理错误(通过记录或任何事情)。

这也大大简化了代码:

function getStuff() { 
  return fetchStuff().then(stuff => process(stuff));
}

async function getStuff() { 
  const stuff = await fetchStuff();
  return process(stuff);
}

如果您坚持使用拒绝原因(记录,修改信息)做某事,请确保重新抛出错误:

function getStuff() { 
  return fetchStuff().then(stuff =>
    process(stuff)
  ).catch(error => {
    stuffDetails.log(error);
    throw new Error("something happened, see detail log");
  });
}

async function getStuff() {
  try {
    const stuff = await fetchStuff();
    return process(stuff);
  } catch(error) {
    stuffDetails.log(error);
    throw new Error("something happened, see detail log");
  }
}

如果您正在处理一些错误,则相同:

function getStuff() { 
  return fetchStuff().then(stuff =>
    process(stuff)
  ).catch(error => {
    if (expected(error))
      return defaultStuff;
    else
      throw error;
  });
}

async function getStuff() {
  try {
    const stuff = await fetchStuff();
    return process(stuff);
  } catch(error) {
    if (expected(error))
      return defaultStuff;
    else
      throw error;
  }
}

答案 2 :(得分:3)

除非绝对需要(永远不会),否则你不应该catch错误的原因是

  

除了吞下承诺拒绝之外,catch处理程序还会吞下任何JS错误   发生在由相应的成功处理程序运行的任何连续代码中。

<强>启示

  1. 一旦catch处理程序捕获到错误,就会将其视为已完成并已处理。承诺链中的所有连续承诺订阅者将调用其成功处理程序而不是失败或捕获处理程序。这导致了奇怪的行为。这绝不是预期的代码流。

  2. 如果像服务方法(getStuff)这样的较低级别的函数处理catch中的错误,则会违反关注点分离的原则。服务处理程序的职责应该仅仅是获取数据。当该数据调用失败时,调用该服务处理程序的应用程序应该管理该错误。

  3. 在某个函数中捕获错误被另一个函数捕获,导致各种奇怪的行为,并且很难跟踪错误的根本原因。要跟踪此类错误,我们必须启用Chrome开发控制台中的Break on Caught Exceptions,该控制台将在每个catch处启用,并且可能需要数小时才能进行调试。

  4. 处理承诺拒绝总是一个好习惯,但我们应该始终使用failure处理程序上的catch处理程序。失败处理程序只会捕获Promise rejections并让应用程序中断,如果发生任何JS错误,应该是这样。

答案 3 :(得分:2)

错误太笼统了,它可以解决所有问题,但是只有很多事情会导致操作失败,错误就是errorSomethingSpecific赋予粒度的一切

答案 4 :(得分:0)

此处最常见的声明适用于javascript以外的语言,除非您打算' ,否则不会“ 捕捉 ”错误处理 '错误。记录处理。

即。一般来说,捕获的最佳(唯一?)原因是以 建设性 方式处理/“处理”错误,该方式允许代码继续执行进一步的问题再一次,一行记录可能永远不会实现......