仅针对错误案例拒绝承诺?

假设我有这个函数authenticate返回一个promise。然后承诺将结果解决。正如我所看到的那样,虚假和真实是预期的结果,拒绝应该仅在错误情况下发生。或者,认证失败是否被视为拒绝承诺?

20

5 答案

好问题!没有硬答案。这取决于您认为异常流程的特定点

拒绝 Promise 与提出异常是一样的。并非所有不受欢迎的结果都是例外错误的结果。你可以两种方式论证你的情况:

  1. 拒绝 Promise 时验证失败,因为调用者期望返回 User 对象,而其他任何东西都是这个流程的例外情况。

  2. 解析 Promise 时,如果 null ,则认证失败,因为提供错误的凭据并不是真正的例外情况,调用者不应指望流总是产生 User

请注意,我正在查看来自来电方的问题。在信息流中,调用者期望他的行为导致 User (以及其他任何错误),或者对于此特定调用者是否有意义处理其他结果?

在多层系统中,答案可能会随着数据流经层而改变。例如:

  • HTTP层说RESOLVE!发送请求,套接字干净,服务器发出有效响应。 Fetch API 可以做到这一点。
  • 协议层然后说REJECT!响应中的状态代码是401,这对于HTTP是可以的,但不适用于协议!
  • 验证层说不,解决!它捕获错误,因为401是错误密码的预期状态,并解析为 null 用户。
  • 接口控制器说没有,拒绝!屏幕上显示的模式是期待用户名和头像,此时除此信息之外的任何内容都是错误。

这个4点的例子显然很复杂,但它说明了两点:

  1. 是否有异常/拒绝取决于周围的流量和期望
  2. 您的计划的不同图层可能会以不同的方式处理相同的结果,因为它们位于不同的流程阶段

所以,再一次,没有硬答案。时间思考和设计!

17
额外

所以Promises有一个很好的属性,他们从函数式语言中带来JS,这就是他们确实实现了这个或者类型构造函数,它将两个其他类型, Left 类型和 Right 类型,强制逻辑采用一个分支或另一个分支。

data Either x y = Left x | Right y

现在你确实注意到左侧的类型对于承诺是模棱两可的;你可以拒绝任何事情。这是 true 因为JS是弱类型的,但是如果你是在防御性编程,那么你要谨慎

原因是JS将从promise处理代码中获取 throw 语句,并将其捆绑到 Left 方面。从技术上讲,你可以抛出所有内容,包括true/false或字符串或数字:但 JavaScript代码也会抛出没有 throw 的东西(当你做的事情,比如尝试访问nulls上的属性),并且有一个已解决的API( Error 对象)。因此,当您开始捕捉时,通常可以假设这些错误是 Error 对象。并且由于promise的 reject 会聚集任何上述错误的错误,你通常只想抛出其他错误,使你的 catch 语句具有简单,一致的逻辑。

因此,虽然您可以catch 中添加if条件并查找错误错误,但在这种情况下,真实情况是微不足道的,

Either (Either Error ()) ()

您可能更喜欢逻辑结构,至少对于来自验证器的立即更简单的布尔值:

Either Error Bool

实际上,下一级别的身份验证逻辑可能会返回某种包含经过身份验证的用户的 User 对象,因此这会变为:

Either Error (Maybe User)

and this is more or less what I'd expect: return null in the case where the user is not defined, otherwise return {user_id: , permission_to_launch_missiles: }. I'd expect that the general case of not being logged in is salvageable, for example if we're in some sort of "demo to new clients" mode, and should not be mixed in with bugs where I accidentally called object.doStuff() when object.doStuff was undefined.

现在说,您可能想要做的是定义 NotLoggedInPermissionError 异常,该异常派生自 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.`);
        }
    });
}
6
额外

错误

Let's talk about 错误.

There are two types of 错误:

  • expected 错误
  • unexpected 错误
  • off-by-one 错误

Expected 错误

Expected 错误 are states where the wrong thing happens but you know that it might, so you deal with it.

这些是用户输入或服务器请求。您知道用户可能犯了错误或服务器可能已关闭,因此您编写一些检查代码以确保程序再次请求输入,或显示消息或其他任何适当的行为。

These are recoverable when handled. If left uhandled, they become unexpected 错误.

Unexpected 错误

Unexpected 错误 (bugs) are states where the wrong thing happens because the code is wrong. You know that they'll eventually happen, but there's no way to know where or how to deal with them because, by definition, they're unexpected.

These are things like syntax and logic 错误. You may have a typo in your code, you may have called a function with the wrong parameters. These aren't typically recoverable.

<�代码>在try..catch </代码>

Let's talk about <�代码>在try..catch </代码>.

在JavaScript中, throw 不常用。如果你在代码中寻找代码,它们将会很少和很远,并且通常是按照这些方式构建的

function example(param) {
  if (!Array.isArray(param) {
    throw new TypeError('"param" should be an array!');
  }
  ...
}

Because of this, <�代码>在try..catch </代码> blocks aren't all that common for control flow either. It's usually pretty easy to add some checks before calling methods to avoid expected 错误.

JavaScript environments are pretty forgiving as well, so unexpected 错误 are often left uncaught as well.

<�代码>在try..catch </代码> doesn't have to be uncommon. There are some nice use cases, which are more common in languages such as Java and C#. Java and C# have the advantage of typed catch constructs, so that you can differentiate between expected and unexpected 错误:

C#:
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;
  }
}

不那么优雅,这是它不常见的部分原因。

功能

Let's talk about 功能.

如果您使用单一责任原则,则每个类别和功能应该用于单一目的。

例如, 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;
  }
}

两者都是可以接受的。

承诺

Let's talk about 承诺.

承诺 are an asynchronous form of <�代码>在try..catch </代码>. Calling new Promise or Promise.resolve starts your try code. Calling throw or Promise.reject sends you to the catch code.

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;
    }
  });

两者都是可以接受的。

嵌套

Let's talk about 嵌套.

<�代码>在try..catch </代码> can be nested. Your authenticate() method might internally have a <�代码>在try..catch </代码> block such as:

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
  }
}

Likewise 承诺 can be nested. Your async authenticate() method might internally use 承诺:

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
    }
  });

那么答案是什么?

好的,我认为现在是时候回答这个问题:

认证失败是否被视为拒绝承诺?

我能给出的最简单的答案是,如果它是同步代码,你应该拒绝承诺抛出异常的任何地方。

如果您的控制流程更简单,只需要,如果检查 then 语句,就不需要拒绝承诺。

If your control flow is simpler by rejecting a promise and then checking for types of 错误 in your error handling code, then do that instead.

3
额外

我使用了Promise的“reject”分支来表示jQuery UI对话框的“取消”操作。它似乎比使用“resolve”分支更自然,尤其是因为对话框上经常有多个“关闭”选项。

0
额外
我认识的大多数纯粹主义者都不同意你的意见。
额外 作者 Gambrinus,

处理承诺或多或少像“if”条件。如果身份验证失败,您是否要“解决”或“拒绝”取决于您。

0
额外
promise是异步 try..catch ,而不是 if
额外 作者 zzzzBov,