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

Ben*_*aum 58 javascript try-catch node.js promise

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

function getStuff() { 
  return fetchStuff().then(stuff => 
    process(stuff)
  ).catch(err => {
    console.error(err);
  });
}
Run Code Online (Sandbox Code Playgroud)

要么:

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

我这样做是为了避免错过错误但是同事用户告诉我不应该这样做而且不赞成.

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

Ben*_*aum 53

为什么旧代码会这样做?

从历史上看,年龄较大(2013年之前)承诺图书馆"吞噬"未经处理的承诺拒绝你自己没有处理过.从那以后的任何事情都不是这种情况.

今天发生了什么?

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

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

// undefined if there was an error
getStuff().then(stuff => console.log(stuff)); 
Run Code Online (Sandbox Code Playgroud)

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

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.
  }
}
Run Code Online (Sandbox Code Playgroud)

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

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

该怎么做:

没有.这就是它的美丽 - 如果你写道:

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

首先,无论如何你都会得到更好的错误:)

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

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

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

  • `process.on('unhandledRejection',...)`......从来没有想过要找这个存在.谢谢!!!! (3认同)
  • @Itay感谢您的评论.浏览器和节点会将错误记录为异常 - 包括文件号,文件名等.请注意,重新抛出(即,}} catch(e){attachMetadata(e); throw e;}`)为了附加元数据而不是`console.error`.这是否解决了您的问题? (2认同)

Ber*_*rgi 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…";
}
Run Code Online (Sandbox Code Playgroud)

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

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

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

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

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

我该怎么做呢?

在对其调用者return的承诺的函数中,不要记录错误并通过这样做来吞下它们.只需返回promise,以便调用者可以捕获拒绝并适当地处理错误(通过记录或任何事情).

这也大大简化了代码:

function getStuff() { 
  return fetchStuff().then(stuff => process(stuff));
}
Run Code Online (Sandbox Code Playgroud)

async function getStuff() { 
  const stuff = await fetchStuff();
  return process(stuff);
}
Run Code Online (Sandbox Code Playgroud)

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

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

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

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

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

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