每个 Promise-chain -> try/catch/await 指南都在教导危险的约定吗? [关闭]

Posted

技术标签:

【中文标题】每个 Promise-chain -> try/catch/await 指南都在教导危险的约定吗? [关闭]【英文标题】:Is every Promise-chain -> try/catch/await guide teaching a dangerous convention? [closed] 【发布时间】:2021-10-16 07:48:08 【问题描述】:

我见过的从捕获错误的 Promise 链到 try/catch/await 的每个示例都如下所示:

class Api 
  static getUserPhones(userObj) 
    console.log(userObj.phones.length)
    return new Promise((resolve, reject) => reject('err'))
  

const user =  id: 123, phones: [] 

/** Turn this... */
Api.getUserPhones(user.id) // user.id is a mistake
 .then(resp => console.log(resp))
 .catch(err => console.error('network error:', err))

/** ...into this! */
try 
  const resp = await Api.getUserPhones(user.id) // user.id is a mistake
  console.log(resp)
 catch (err) 
  console.error('network error:', err)


但这在最坏的情况下是不安全的,并且充其量是为我们设置错误。任何TypeErrorReferenceErrorSyntaxErrorRangeError 等可能发生在Api.getUser() 中,甚至在传递给它的参数(user.wrong.id)中,都会意外吞入try 块中.除非您的代码始终没有错误,否则将代码转换为 try/catch(如示例)可能会使查找错误和调试它们变得非常具有挑战性。

作为一个极端的例子,如果指南告诉我们将所有您的代码放在 try 中,那么 IMO 的指南将是错误的。一切都可以抛出,即使不是设计为,我们应该只在我们的代码按设计抛出时使用try

我错过了什么吗?是不是每个人都只是在编写风险更高的代码而不自知?

【问题讨论】:

我认为假设代码是合理的(例如,返回 Promise 而不是 throw)是合理的,尽管它们并不完全等价。 假设Api.getUser() 中的所有内容都将永远不会出现javascript错误,这是非常愚蠢的。 "但这不安全" 当然不是,没有分号可以保护你!不过,严肃地说,如果您能澄清“安全”和“不安全”的含义,那将是一件好事。您认为转换后的代码出于某种原因本质上是不安全的,还是只是它与原始代码不完全等效? 没有必要居高临下。我希望其他人不要陷入我刚刚遇到的相同漏洞,并确保我理解问题,以一致的方式克服它,看看其他人是否有其他解决方案来避免这些错误。在我告诉其他人他们可能在不知不觉中承担的风险之前,我试图确保我已经收集了关于该主题的足够知识。这不是SO的重点吗?互相帮助写出更好的代码? 【参考方案1】:

两个代码 sn-ps 在行为上并不总是 100% 相同这一事实并不会使它们中的任何一个都不安全。它只是使它们在特定的边缘情况下有所不同。最后,是否需要保留这种边缘情况,取决于代码作者。

也就是说,适当的等价物只是在 try/catch 之外获得承诺:

const promise = Api.getUser(user.id)
try 
  const resp = await promise
  console.log(resp)
 catch (err) 
  console.error(err)

但是,从这里我们可以根据我们可以做出的假设进行一些修改。例如,如果我们知道 Api.getUser(user.id) 不会抛出,那么在 try 块内调用 Api.getUser(user.id) 是完全有效的。

作为一个未成年人,我还想就这个问题发表评论:

假设 Api.getUser() 中的所有内容都将永远不会出现 javascript 错误,这是非常愚蠢的

这是一个强有力的断言,它也恰好是明显错误的。首先,让我们明确一点,并非Api.getUser() 中的所有内容都需要没有错误。只要在Api.getUser() 内处理了任何错误,调用者可能仍然无法处理这些错误。其次,如果Api.getUser() 被记录为不会抛出,那么期望它不会抛出将是非常合理的事情。否则,您必须做出判断调用是否重要,是否可以抛出,以及这些错误是否需要与通过 Promise 传播的错误区别对待。

【讨论】:

1. “如果我们知道 Api.getUser(user.id) 不会抛出”。除非您编写完美的代码,否则一切都“可以”抛出。 2.“只要在 Api.getUser() 内部处理了任何错误”。你会在 Api.getUser() 中写一个 try/catch 来处理潜在的TypeErrors 吗?我当然不会。我会按照它的工作方式编写代码,如果调用者错误地使用函数,让错误传播。 "除非你写出完美的代码,否则一切都'可以'抛出。"不需要完美。自动化测试和其他工具可以帮助你很多。 “你会在 Api.getUser() 中编写一个 try/catch 来处理潜在的 TypeErrors 吗?”可能是。或者也许我有足够的类型检查来避免 TypeErrors 的可能性。 “我当然不会。”很高兴你决定了。但你不这样做并不妨碍其他人这样做,也不会使它变得不合理。 “我会按照它应该工作的方式编写代码,如果调用者不正确地使用函数,就会让错误传播。”当然,传播这些错误是一个非常好的选择。但是“打算”工作取决于相关功能的设计。如果作者的意思是让它不根据输入抛出错误,那么抛出错误并不是它的本意。 关于用户输入,请记住Api.getUser() 本身可能并不关心输入的类型。例如,任何验证都可能已经在 Promise 下进行,而不是在 Api.getUser() 中。【参考方案2】:

try/catch 块的工作方式是,如果在try 块中抛出任何错误,它将在catch 块中处理。

答案是否定的,他们没有错。

TypeErrors 未被“吞噬”。它们的处理方式与任何其他错误一样。 这是一个JSFiddle 说明这一点。

【讨论】:

你没有抓住重点。 @KFunk 也许你应该澄清你的意思。【参考方案3】:

似乎指南应该告诉我们写:

const annoyingPromiseVar = Api.getUserPhones(user.id) // behaves like promise chains. won't hide Errors
try 
  const resp = await annoyingPromiseVar
  console.log(resp)
 catch (err) 
  console.error(err)

【讨论】:

你误解了 async 和 await

以上是关于每个 Promise-chain -> try/catch/await 指南都在教导危险的约定吗? [关闭]的主要内容,如果未能解决你的问题,请参考以下文章

在每个 [重复] 创建 tr 时,在奇数和偶数之间添加不同的背景颜色

Linux常用命令——tr

1: unit test

使用表主类在 tr 之间获取表中的所有 tr 并推入数组

jQuery 每个“睡眠”都带有回调

hdu1575 Tr A