假设我有此函数authenticate可以返回承诺。然后,诺言就会解决结果。如我所见,假和真是预期的结果,并且拒绝仅应在错误情况下发生。或者,是否认为身份验证失败会导致您无法兑现承诺?
false
还是引发异常?
假设我有此函数authenticate可以返回承诺。然后,诺言就会解决结果。如我所见,假和真是预期的结果,并且拒绝仅应在错误情况下发生。或者,是否认为身份验证失败会导致您无法兑现承诺?
false
还是引发异常?
Answers:
好问题!没有硬的答案。这取决于你认为什么是例外的,在流动的具体点。
拒绝a等同Promise
于引发异常。并非所有不想要的结果都是例外,都是错误的结果。您可以通过两种方式争论您的情况:
失败的身份验证应该reject
是Promise
,因为调用者期望User
返回一个对象,而其他任何情况都是该流程的例外。
认证失败应该resolve
的Promise
,尽管来null
,因为提供了错误的凭证是不是一个真正的特殊情况下,调用者不应该期望流量总是导致User
。
请注意,我正在从调用方的角度查看问题。在信息流中,呼叫者是否期望他的行为导致User
(并且其他任何情况都是错误),或者这个特定的呼叫者处理其他结果是否有意义?
在多层系统中,答案可能会随着数据流经各层而改变。例如:
null
用户。这个4点的例子显然很复杂,但是它说明了2点:
再说一遍,没有很难的答案。是时候思考和设计了!
因此,Promises具有一个不错的特性,即它们从功能语言中引入了JS,这是因为它们确实实现了这种Either
类型构造器,该类型构造器通过强制逻辑采用一个分支或另一个来将两个其他类型(Left
类型和类型)粘合在一起Right
科。
data Either x y = Left x | Right y
现在,您实际上已经注意到,左侧的类型对于Promise而言是模棱两可的。你可以拒绝任何东西。这是正确的,因为JS的类型很弱,但是如果您要进行防御性编程,则要谨慎。
原因是JS throw
将从承诺处理代码中获取语句,并将其捆绑到其Left
一边。从技术上讲,在JS中,您可以进行throw
所有操作,包括对/错,字符串或数字:但是JavaScript代码也不会抛出任何错误throw
(当您执行诸如尝试访问null的属性之类的操作时),并且为此存在一个已解决的API(Error
对象) 。因此,当您开始捕捉时,通常可以假设那些错误是Error
对象是一件好事。而且由于reject
for promise会在上述任何错误中引起任何错误,因此您通常只希望throw
其他错误,以使您的catch
语句具有简单,一致的逻辑。
因此,尽管您可以在条件中加入if-condition catch
并查找错误错误,但在这种情况下,真实情况微不足道,
Either (Either Error ()) ()
您可能会更喜欢布尔结构的逻辑结构,至少对于从身份验证器中直接得到的结构,至少是这样:
Either Error Bool
实际上,认证逻辑的下一层可能是返回某种User
包含已认证用户的对象,因此它将变为:
Either Error (Maybe User)
这或多或少是我所期望的:null
如果未定义用户,则返回,否则返回{user_id: <number>, permission_to_launch_missiles: <boolean>}
。我希望没有登录的一般情况是可以挽救的,例如,如果我们处于某种“向新客户演示”模式,并且不应与我不小心调用object.doStuff()
when object.doStuff
was的错误混合在一起undefined
。
话虽如此,您可能想要定义一个NotLoggedIn
或PermissionError
派生自的异常Error
。然后在真正需要的东西中,您要编写:
function launchMissiles() {
function actuallyLaunchThem() {
// stub
}
return getAuth().then(auth => {
if (auth === null) {
throw new PermissionError('Cannot launch missiles without permission, cannot have permission if not logged in.');
} else if (auth.permission_to_launch_missiles) {
return actuallyLaunchThem();
} else {
throw new PermissionError(`User ${auth.user_id} does not have permission to launch the missiles.`);
}
});
}
让我们谈谈错误。
错误有两种:
预期的错误是发生错误的事情的状态,但是您知道它可能会发生,因此您可以对其进行处理。
这些是用户输入或服务器请求之类的东西。您知道用户可能会犯错或者服务器可能已关闭,因此您编写了一些检查代码来确保程序再次要求输入或显示消息,或采取其他适当的措施。
处理后可以恢复。如果不加以处理,它们将成为意外错误。
意外错误(错误)是由于代码错误而发生错误的状态。您知道它们最终会发生,但是无法知道在哪里或如何处理它们,因为按照定义,它们是意外的。
这些就是语法和逻辑错误。您的代码中可能有错字,您可能用错误的参数调用了函数。这些通常是不可恢复的。
try..catch
让我们来谈谈try..catch
。
在JavaScript中,throw
它不常用。如果您在代码中四处寻找示例,它们之间的距离将会很少,而且通常会按照
function example(param) {
if (!Array.isArray(param) {
throw new TypeError('"param" should be an array!');
}
...
}
因此,try..catch
对于控制流而言,块也不是那么普遍。通常很容易在调用方法之前添加一些检查,以避免预期的错误。
JavaScript环境也很宽容,因此通常也不会遗漏意外错误。
try..catch
不一定非同寻常。有一些很好的用例,在Java和C#等语言中更常见。Java和C#具有类型catch
构造的优点,因此您可以区分预期错误和意外错误:
try
{
var example = DoSomething();
}
catch (ExpectedException e)
{
DoSomethingElse(e);
}
此示例允许其他意外异常聚集并在其他地方进行处理(例如通过记录并关闭程序)。
在JavaScript中,可以通过以下方式复制此构造:
try {
let example = doSomething();
} catch (e) {
if (e instanceOf ExpectedError) {
DoSomethingElse(e);
} else {
throw e;
}
}
不那么优雅,这是它不常见的部分原因。
让我们谈谈功能。
如果您使用单一职责原则,则每个类和功能都应具有单个目的。
例如,authenticate()
可以验证用户身份。
可以这样写:
const user = authenticate();
if (user == null) {
// keep doing stuff
} else {
// handle expected error
}
另外,它也可以写成:
try {
const user = authenticate();
// keep doing stuff
} catch (e) {
if (e instanceOf AuthenticationError) {
// handle expected error
} else {
throw e;
}
}
两者都是可以接受的。
让我们谈谈诺言。
承诺是的异步形式try..catch
。调用new Promise
或Promise.resolve
启动您的try
代码。调用throw
或Promise.reject
将您发送到catch
代码。
Promise.resolve(value) // try
.then(doSomething) // try
.then(doSomethingElse) // try
.catch(handleError) // catch
如果您具有异步功能来验证用户身份,则可以将其编写为:
authenticate()
.then((user) => {
if (user == null) {
// keep doing stuff
} else {
// handle expected error
}
});
另外,它也可以写成:
authenticate()
.then((user) => {
// keep doing stuff
})
.catch((e) => {
if (e instanceOf AuthenticationError) {
// handle expected error
} else {
throw e;
}
});
两者都是可以接受的。
让我们谈谈嵌套。
try..catch
可以嵌套。您的authenticate()
方法内部可能有一个try..catch
块,例如:
try {
const credentials = requestCredentialsFromUser();
const user = getUserFromServer(credentials);
} catch (e) {
if (e instanceOf CredentialsError) {
// handle failure to request credentials
} else if (e instanceOf ServerError) {
// handle failure to get data from server
} else {
throw e; // no idea what happened
}
}
同样,可以嵌套诺言。您的异步authenticate()
方法可能在内部使用Promise:
requestCredentialsFromUser()
.then(getUserFromServer)
.catch((e) => {
if (e instanceOf CredentialsError) {
// handle failure to request credentials
} else if (e instanceOf ServerError) {
// handle failure to get data from server
} else {
throw e; // no idea what happened
}
});
好的,我认为是时候该回答这个问题了:
身份验证失败是否被认为您会拒绝承诺?
我能给出的最简单的答案是,您应该在任何希望throw
同步异常的异常情况下拒绝承诺。
如果通过if
在then
语句中进行一些检查来简化控制流程,则无需拒绝承诺。
如果通过拒绝承诺然后检查错误处理代码中的错误类型来简化控制流程,则可以这样做。
处理承诺或多或少类似于 “如果”条件。如果身份验证失败,则要由您决定是“解决”还是“拒绝”。
try..catch
,不是if
。
try { if (!doSomething()) throw whatever; doSomethingElse() } catch { ... }
很好,但是a Promise
表示的构造是try..catch
部分,而不是if
部分。
doSomething()
失败,它将抛出,但是如果失败,它将包含您需要的值(if
上面的内容有点令人困惑,因为它不是您的想法的一部分:))。仅在有理由抛出类推的情况下才应拒绝,因此如果测试失败。如果测试成功,则无论其值是否为正,您都应始终解决,对吗?
reject
也不应该返回false,但是如果期望该值是aBool
,那么您就成功了,并且无论该值如何,都应该使用Bool进行解析。承诺是值的一种代理-它们存储返回的值,因此,只有在无法获得该值的情况下,您才可以reject
。否则你应该resolve
。