每个 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)
但这在最坏的情况下是不安全的,并且充其量是为我们设置错误。任何TypeError
、ReferenceError
、SyntaxError
、RangeError
等可能发生在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 来处理潜在的TypeError
s 吗?我当然不会。我会按照它的工作方式编写代码,如果调用者错误地使用函数,让错误传播。
"除非你写出完美的代码,否则一切都'可以'抛出。"不需要完美。自动化测试和其他工具可以帮助你很多。 “你会在 Api.getUser() 中编写一个 try/catch 来处理潜在的 TypeErrors 吗?”可能是。或者也许我有足够的类型检查来避免 TypeErrors 的可能性。 “我当然不会。”很高兴你决定了。但你不这样做并不妨碍其他人这样做,也不会使它变得不合理。
“我会按照它应该工作的方式编写代码,如果调用者不正确地使用函数,就会让错误传播。”当然,传播这些错误是一个非常好的选择。但是“打算”工作取决于相关功能的设计。如果作者的意思是让它不根据输入抛出错误,那么抛出错误并不是它的本意。
关于用户输入,请记住Api.getUser()
本身可能并不关心输入的类型。例如,任何验证都可能已经在 Promise 下进行,而不是在 Api.getUser()
中。【参考方案2】:
try/catch
块的工作方式是,如果在try
块中抛出任何错误,它将在catch
块中处理。
答案是否定的,他们没有错。
TypeError
s 未被“吞噬”。它们的处理方式与任何其他错误一样。
这是一个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 指南都在教导危险的约定吗? [关闭]的主要内容,如果未能解决你的问题,请参考以下文章