javascript 如何修复“在本地捕获的异常的'抛出'”?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/47015693/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-29 07:07:15  来源:igfitidea点击:

How to fix "'throw' of exception caught locally"?

javascriptnode.jsrestexception-handlingasync-await

提问by cib

In this function that handles a REST API call, any of the called functions to handle parts of the request might throw an error to signal that an error code should be sent as response. However, the function itself might also discover an error, at which point it should jump into the exception handling block.

在此处理 REST API 调用的函数中,任何处理请求部分的被调用函数都可能抛出错误以表示应发送错误代码作为响应。但是,函数本身也可能会发现错误,此时它应该跳转到异常处理块。

static async handleRequest(req) {
    try {
        let isAllowed = await checkIfIsAllowed(req);
        if (!isAllowed) {
            throw new ForbiddenException("You're not allowed to do that.");
        }
        let result = await doSomething(req); // can also raise exceptions
        sendResult(result);
    } catch(err) {
        sendErrorCode(err);
    }
}

Webstorm will underline the throwwith the following message: 'throw' of exception caught locally. This inspection reports any instances of JavaScript throw statements whose exceptions are always caught by containing try statements. Using throw statements as a "goto" to change the local flow of control is likely to be confusing.

Webstorm 将throw在下面的消息下划线:'throw' of exception caught locally. This inspection reports any instances of JavaScript throw statements whose exceptions are always caught by containing try statements. Using throw statements as a "goto" to change the local flow of control is likely to be confusing.

However, I'm not sure how to refactor the code to improve the situation.

但是,我不确定如何重构代码以改善这种情况。

I could copypaste the code from the catchblock into the ifcheck, but I believe this would make my code less readable and harder to maintain.

我可以将代码catch块中的代码复制粘贴到if检查中,但我相信这会使我的代码可读性降低且难以维护。

I could write a new function that does the isAllowedcheck and throws an exception if it doesn't succeed, but that seems to be sidestepping the issue, rather than fixing a design problem that Webstorm is supposedly reporting.

我可以编写一个新函数来执行isAllowed检查并在不成功时抛出异常,但这似乎是在回避问题,而不是修复 Webstorm 据称报告的设计问题。

Are we using exceptions in a bad way, and that's why we're encountering this problem, or is the Webstorm error simply misguiding and should be disabled?

我们是否以错误的方式使用异常,这就是我们遇到这个问题的原因,还是 Webstorm 错误只是误导而应该被禁用?

采纳答案by James Thorpe

You're checking for something and throwing an exception if isAllowedfails, but you know what to do in that situation - call sendErrorCode. You should throw exceptions to external callers if you don't know how to handle the situation - ie in exceptional circumstances.

您正在检查某些内容并在isAllowed失败时抛出异常,但您知道在这种情况下该怎么做 - 调用sendErrorCode. 如果您不知道如何处理这种情况 - 即在特殊情况下,您应该向外部调用者抛出异常。

In this case you already have a defined process of what to do if this happens - just use it directly without the indirect throw/catch:

在这种情况下,您已经有一个定义的过程,如果发生这种情况该怎么办 - 只需直接使用它而无需间接抛出/捕获:

static async handleRequest(req) {
    try {
        let isAllowed = await checkIfIsAllowed(req);
        if (!isAllowed) {
            sendErrorCode("You're not allowed to do that.");
            return;
        }
        let result = await doSomething(req); // can also raise exceptions
        sendResult(result);
    } catch(err) {
        sendErrorCode(err);
    }
}

I could copypaste the code from the catchblock into the ifcheck, but I believe this would make my code less readable and harder to maintain.

我可以将代码catch块中的代码复制粘贴到if检查中,但我相信这会使我的代码可读性降低且难以维护。

On the contrary, as above, I would expect this to be the way to handle this situation.

相反,如上所述,我希望这是处理这种情况的方式。

回答by J. Munson

Contrary to James Thorpe's opinion, I slightly prefer the pattern of throwing. I don't see any compelling reason to treat local errors in the try block any differently from errors that bubble up from deeper in the call stack... just throw them. In my opinion, this is a better application of consistency.

与 James Thorpe 的观点相反,我稍微更喜欢投掷的模式。我没有看到任何令人信服的理由来对待 try 块中的本地错误与从调用堆栈更深处冒出的错误有任何不同……只是抛出它们。在我看来,这是一致性的更好应用。

Because this pattern is more consistent, it naturally lends itself better to refactoring when you want to extract logic in the try block to another function that is perhaps in another module/file.

因为这种模式更加一致,所以当您想要将 try 块中的逻辑提取到可能位于另一个模块/文件中的另一个函数时,它自然更适合重构。

// main.js
try {
  if (!data) throw Error('missing data')
} catch (error) {
  handleError(error)
}

// Refactor...

// validate.js
function checkData(data) {
  if (!data) throw Error('missing data')
}

// main.js
try {
  checkData(data)
} catch (error) {
  handleError(error)
}

If instead of throwing in the try block you handle the error, then the logic has to change if you refactor it outside of the try block.

如果不是在 try 块中处理错误,而是在 try 块之外对其进行重构,则逻辑必须更改。

In addition, handling the error has the drawback of making you remember to return early so that the try block doesn't continue to execute logic after the error is encountered. This can be quite easy to forget.

此外,处理错误的缺点是让您记得提前返回,这样 try 块在遇到错误后不会继续执行逻辑。这很容易忘记。

try {
  if (!data) {
    handleError(error)
    return // if you forget this, you might execute code you didn't mean to. this isn't a problem with throw.
  }
  // more logic down here
} catch (error) {
  handleError(error)
}

If you're concerned about which method is more performant, you shouldn't be. Handling the error is technically more performant, but the difference between the two is absolutely trivial.

如果您担心哪种方法性能更好,那么您不应该担心。处理错误在技术上更高效,但两者之间的区别绝对微不足道。

Consider the possibility that WebStorm is a bit too opinionated here. ESLint doesn't even have a rule for this. Either pattern is completely valid.

考虑一下 WebStorm 在这里有点过于固执的可能性。ESLint 对此甚至没有规定。这两种模式都是完全有效的。

回答by SlowSuperman

Since this is not a blocking error, but only an IDE recommendation, then the question should be viewed from two sides.

既然这不是阻塞错误,而只是IDE建议,那么问题应该从两个方面来看。

The first side is performance. If this is a bottleneck and it is potentially possible to use it with compilation or when transferring to new (not yet released) versions of nodejs, the presence of repetitions is not always a bad solution. It seems that the IDE hints precisely in this case and that such a design can lead to poor optimization in some cases.

第一面是性能。如果这是一个瓶颈,并且有可能将其用于编译或转移到新的(尚未发布的)nodejs 版本时,重复的存在并不总是一个糟糕的解决方案。在这种情况下,IDE 似乎准确地暗示了这种设计,并且在某些情况下,这种设计可能会导致优化不佳。

The second side is the code design. If it will make the code more readable and simplify the work for other developers - keep it. From this point of view, solutions have already been proposed above.

第二个方面是代码设计。如果它会使代码更具可读性并简化其他开发人员的工作 - 保留它。从这个角度来看,上面已经提出了解决方案。

回答by Old Pro

There are good answers to the question "Why not use exceptions as normal flow control?" here.

“为什么不使用异常作为正常的流量控制?”这个问题有很好的答案。在这里

The reason not to throw an exception that you will catch locally is that you locally know how to handle that situation, so it is, by definition, not exceptional.

不抛出您将在本地捕获的异常的原因是您在本地知道如何处理这种情况,因此根据定义,这不是异常。

@James Thorpe's answerlooks good to me, but @matchish feels it violates DRY. I say that in general, it does not. DRY, which stands for Don't Repeat Yourself, is defined by the people who coined the phrase as "Every piece of knowledge must have a single, unambiguous, authoritative representation within a system". As applied to writing software code, it is about not repeating complexcode.

@James Thorpe 的回答对我来说看起来不错,但 @matchish 觉得它违反了 DRY。我说一般来说,它没有。DRY 代表 Don't Repeat Yourself,由创造这句话的人定义为“每一条知识都必须在系统内有一个单一的、明确的、权威的表示”。当应用于编写软件代码时,它是关于不重复复杂的代码。

Practically any code that is said to violate DRY is said to be "fixed" by extracting the repeated code into a function and then calling that function from the places it was previously repeated. Having multiple parts of your code call sendErrorCodeis the solutionto fixing a DRY problem. All of the knowledge of what to do with the error is in one definitive place, namely the sendErrorCodefunction.

实际上,通过将重复的代码提取到函数中,然后从先前重复的位置调用该函数,可以说任何违反 DRY 的代码都被称为“修复”。代码调用的多个部分sendErrorCode解决DRY 问题的解决方案。关于如何处理错误的所有知识都集中在一个明确的地方,即sendErrorCode函数。

I would modify @James Thorpe's answer slightly, but it is more of a quibble than a real criticism, which is that sendErrorCodeshould be receiving exception objects or strings but not both:

我会稍微修改@James Thorpe 的答案,但这更像是一种狡辩而不是真正的批评,即sendErrorCode应该接收异常对象或字符串,但不能同时接收两者:

static async handleRequest(req) {
    try {
        let isAllowed = await checkIfIsAllowed(req);
        if (!isAllowed) {
            sendErrorCode(new ForbiddenException("You're not allowed to do that."));
            return;
        }
        let result = await doSomething(req); // can also raise exceptions
        sendResult(result);
    } catch(err) {
        sendErrorCode(err);
    }
}

The larger question is what is the likelihood of the error and is it appropriate to treat !isAllowedas an exception. Exceptions are meant to handle unusual or unpredictable situations. I would expect !isAllowedto be a normal occurrence that should be handled with logic specific to that situation, unlike, say, a sudden inability to query the database that has the answer to the isAllowedquestion.

更大的问题是错误的可能性有多大,是否适合!isAllowed作为例外对待。异常旨在处理异常或不可预测的情况。我希望!isAllowed这是一个正常的事件,应该使用特定于该情况的逻辑来处理,而不像突然无法查询具有isAllowed问题答案的数据库。

@matchish's proposed solutionchanges the contract of doSomethingOnAllowedRequestfrom something that will never throw an exception to something that will routinely throw an exception, placing the burden of exception handling on all of its callers. This is likely to cause a violation of DRY by causing multiple callers to have repetitions of the same error handling code, so in the abstract I do not like it. In practice, it would depend on the overall situation, such as how many callers are there and do they, in fact, share the same response to errors.

@matchish提议的解决方案将契约doSomethingOnAllowedRequest从永远不会抛出异常的事物更改为会例行抛出异常的事物,从而将异常处理的负担置于所有调用者身上。这很可能会导致多个调用者重复相同的错误处理代码,从而违反 DRY,因此在抽象中我不喜欢它。在实践中,这将取决于整体情况,例如有多少调用者以及他们实际上对错误的响应是否相同。

回答by matchish

Answer of James Thorpe has one disadvantage on my opinion. It's not DRY, in both cases when you call sendError you handle Exceptions. Let's imagine we have many lines of code with logic like this where Exception can be thrown. I think it can be better.

詹姆斯索普的回答在我看来有一个缺点。这不是 DRY,在这两种情况下,当您调用 sendError 时,您都会处理异常。让我们想象一下,我们有很多这样的逻辑代码行,其中可以抛出异常。我认为它可以更好。

This is my solution

这是我的解决方案

async function doSomethingOnAllowedRequest(req) {
    let isAllowed = await checkIfIsAllowed(req);
    if (!isAllowed) {
       throw new ForbiddenException("You're not allowed to do that.");
    }
    doSomething(req);
}
static async handleRequest(req) {
    try {
        let result = await doSomethingOnAllowedRequest(req);
        sendResult(result);
    } catch(err) {
        sendErrorCode(err);
    }
}

回答by backslash

Return a promise reject instead of throwing error in the try block

在 try 块中返回一个 promise 拒绝而不是抛出错误

  try {
    const isAllowed = await checkIfIsAllowed(request);

    if (!isAllowed) {
      return Promise.reject(Error("You're not allowed to do that."));
    }

    const result = await doSomething(request);

    sendResult(result);
  } catch (error) {
    throw error;
  }

回答by Di V

This could give you some tips, maybe that can be the cause(not sure if relevant). Catch statement does not catch thrown error

这可以给你一些提示,也许这可能是原因(不确定是否相关)。 Catch 语句不捕获抛出的错误

" The reason why your try catch block is failing is because an ajax request is asynchronous. The try catch block will execute before the Ajax call and send the request itself, but the error is thrown when the result is returned, AT A LATER POINT IN TIME.

" 你的 try catch 块失败的原因是因为 ajax 请求是异步的。try catch 块会在 Ajax 调用之前执行并发送请求本身,但是返回结果时会抛出错误,AT A LATER POINT IN时间。

When the try catch block is executed, there is no error. When the error is thrown, there is no try catch. If you need try catch for ajax requests, always put ajax try catch blocks inside the success callback, NEVER outside of it."

当 try catch 块执行时,没有错误。抛出错误时,没有try catch。如果你需要 try catch 来处理 ajax 请求,总是把 ajax try catch 块放在成功回调里面,永远不要放在它外面。”