代码之家  ›  专栏  ›  技术社区  ›  Benjamin Gruenbaum

为什么不鼓励`.catch(err=>console.error(err))`

  •  58
  • Benjamin Gruenbaum  · 技术社区  · 6 年前

    我使用的是承诺,代码如下:

    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 回复  |  直到 6 年前
        1
  •  53
  •   Mike Chamberlain    6 年前

    为什么旧代码会这样做?

    历史上,较老的(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.
      }
    }
    

    我认为消费者不会期望此功能返回 未定义 如果发生错误。

    所以,总而言之,这是不受欢迎的,因为它让应用程序流中的开发人员吃惊,浏览器记录了今天未捕获的承诺错误。

    相反,该怎么做:

    没有什么。 这就是它的美妙之处-如果你写:

    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));
    
        2
  •  13
  •   Bergi    6 年前

    怎么了? return ….catch(err => console.error(err)) 是吗?

    它会兑现一个承诺 undefined 处理错误后。

    就其本身而言,捕获错误并将其记录在Promise链的末尾是可以的:

    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() 捕获错误本身以记录它,而不做任何其他事情来处理它,就像提供一个明智的回退结果一样,它会导致 未定义 出现在页面中而不是“ 对不起的 “。

    我见过很多这样做的代码,为什么?

    从历史上看,人们害怕承诺错误在任何地方都不会得到处理,从而导致它们完全消失——被承诺“吞没”。所以他们补充说 .catch(console.error) 在每一个函数中确保他们注意到控制台中的错误。

    这不再是必需的,因为所有现代Promise实现都可以检测到未处理的Promise拒绝,并将在控制台上触发警告。

    当然,仍然有必要(或者至少是良好的实践,即使你不希望任何事情失败)发现错误。 最后 承诺链(当你不再返回承诺时)。

    我应该怎么做?

    在函数中, return 承诺 对于他们的呼叫者,不要记录错误并通过这样做来吞下它们。只需返回承诺,这样调用者就可以捕获拒绝并适当地处理错误(通过日志记录或其他方式)。

    这也大大简化了代码:

    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;
      }
    }
    
        3
  •  3
  •   yeshashah    6 年前

    你不应该这样做的原因 catch 除非绝对需要,否则错误是

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

    启示

    1. 一旦错误被 抓住 处理程序,它被视为完成和处理。Promise链中的所有后续Promise订户都将调用其成功处理程序,而不是失败或捕获处理程序。这会导致奇怪的行为。这不是预期的代码流。

    2. 如果较低级别的函数类似于服务方法( getStuff )处理中的错误 抓住 它打破了 关注点分离 .服务处理程序的职责应该仅限于获取数据。当该数据调用失败时,调用该服务处理程序的应用程序应管理该错误。

    3. 在某个函数中捕获错误被另一个函数捕获,会导致周围出现奇怪的行为,使跟踪错误的根本原因变得非常困难。要跟踪此类错误,我们必须启用 Break on Caught Exceptions 在chrome dev控制台中,它将在 抓住 调试可能需要几个小时。

    处理承诺拒绝总是一个很好的实践,但我们应该始终使用 failure 处理程序结束 抓住 处理程序。失败处理程序将只捕获 Promise rejections 如果发生JS错误,让应用程序中断,这是应该的。

        4
  •  2
  •   mmx    6 年前

    错误太普通了,它是一个catch all,但操作失败的原因太多,错误就是所有的错误都是somethingspecific给出的粒度

        5
  •  0
  •   spechter    6 年前

    这里最通用的语句是不适用于Javascript以外的语言 抓住 '错误,除非您计划' 手柄 '错误。日志记录是 处理。

    也就是说,一般来说,最好的(只有?)catch的原因是处理 建设性的 允许代码继续运行而不出现任何问题的方式。再说一次,一系列的伐木可能永远也无法做到这一点…