Node.js 何时拒绝/解决承诺

Node.js 何时拒绝/解决承诺,node.js,promise,q,Node.js,Promise,Q,我在想什么时候我该拒绝一个承诺。 我发现了几个关于这个话题的问题,但找不到合适的答案。 本文 说: 解析:一个成功的承诺是“已解析”的,它调用正在等待的成功侦听器,并记住为附加的未来成功侦听器解析的值。分辨率与返回的值相关 拒绝:当遇到错误条件时,将“拒绝”一个承诺,该承诺将调用正在等待的错误侦听器,并为将来附加的错误侦听器记住被拒绝的值。拒绝与抛出的异常相关 这是否原则指引? 只有在出现例外情况时才拒绝承诺 但是对于一个函数,比如 findUserByEmail() 我希望函数返回一个

我在想什么时候我该拒绝一个承诺。 我发现了几个关于这个话题的问题,但找不到合适的答案。

本文 说:

  • 解析:一个成功的承诺是“已解析”的,它调用正在等待的成功侦听器,并记住为附加的未来成功侦听器解析的值。分辨率与返回的值相关
  • 拒绝:当遇到错误条件时,将“拒绝”一个承诺,该承诺将调用正在等待的错误侦听器,并为将来附加的错误侦听器记住被拒绝的值。拒绝与抛出的异常相关
这是否原则指引? 只有在出现例外情况时才拒绝承诺

但是对于一个函数,比如

findUserByEmail()
我希望函数返回一个用户,这样我就可以在不验证结果的情况下继续链

findUserByEmail()
    .then(sendWelcomeBackEmail)
    .then(doSomeNiceStuff)
    .then(etc..)

什么是最好的/常用的做法?

我知道你来自哪里。Q和Q文档很容易让您相信延迟/承诺拒绝完全是关于异常处理的

情况未必如此

无论您的申请需要什么理由,都可以拒绝延期申请。

延迟/承诺都是关于处理来自异步流程的响应,每个异步流程都可能导致各种结果——其中一些是“成功的”,另一些是“不成功的”。您可以选择拒绝您的延迟-无论出于何种原因,无论结果名义上是成功还是不成功,并且在javascript或异步过程中都不会抛出异常


您还可以选择在异步进程上实现超时,在这种情况下,您可以选择在没有收到响应(成功或不成功)的情况下拒绝延迟进程。事实上,对于超时,您通常会选择这样做。

通常,您可以将拒绝视为类似于同步的
抛出
,并将实现视为类似于同步的
返回
。当函数以某种方式失败时,应该拒绝。这可能是超时、网络错误、输入错误等

拒绝承诺,就像抛出异常一样,对于控制流非常有用。它不一定代表一个真正意想不到的错误;它可以代表您完全预期和处理的问题:

function getProfile(email) {
  return getProfileOverNetwork(email)
    .then(null, function (err) {
      //something went wrong getting the profile
      if (err.code === 'NonExistantUser') {
        return defaultUser;
      } else if (profileCached(email)) {
        return getProfileFromCache(email);//fall back to cached profile
      } else {
        throw err;//sometimes we don't have a nice way of handling it
      }
    })
}
拒绝让我们跳过正常的成功行为,直到我们找到一种知道如何处理它的方法。作为另一个例子,我们可能会有一些函数深入嵌套在应用程序堆栈的底部,它拒绝。这可能要到堆栈的最顶端才能处理,在那里我们可以记录它。关键是拒绝会像同步代码中的异常一样在堆栈中向上移动

通常,只要可能,如果您正在努力编写一些异步代码,您应该考虑“如果这是同步的,我会写什么”。这通常是一个相当简单的转换,从它到承诺的等价物

exists
方法中可以使用拒绝的承诺:

function exists(filePath) {
  return stat(filePath) //where stat gets last updated time etc. of the file
    .then(function () { return true; }, function () { return false; })
}
请注意,在这种情况下,拒绝完全是意料之中的,只是意味着文件不存在。请注意它是如何与同步函数并行的:

function existsSync(filePath) {
  try {
    statSync(filePath);
    return true;
  } catch (ex) {
    return false;
  }
}
你的榜样 回到你的例子:

如果找不到用户,我通常会拒绝由
findUserByEmail
产生的承诺。这是你完全期待有时会发生的事情,但这是正常情况下的例外,应该像处理所有其他错误一样处理。类似地,如果我正在编写一个同步函数,我会让它
抛出一个异常


有时只返回
null
可能很有用,但这取决于您的应用程序逻辑,可能不是最好的方法。

谢谢,正如预期的那样,这更像是一个哲学问题。因为您与要在找不到用户时抛出的同步异常的比较也可能会引起双方的争论。但是,在我的发言中,我同意你的结论。是的,我想表达的观点是,无论是拒绝承诺还是抛出异常,都是同一个论点。这个例子在技术上是正确的,但我建议不要将异常用于流控制。充其量,这会让其他程序员感到困惑。在最坏的情况下,它会使错误报告变得困难,并可能导致性能下降。是否确实希望在每次找不到用户时生成堆栈跟踪?更多信息:@ben这取决于很多事情。性能代价可能是不小的,但只要找不到用户的情况不是太频繁,就可以了。考虑到这是典型的行为在很多JavaScript库中。例如,node.js中的
fs.exists
函数是通过调用
fs.stat
然后处理错误来实现的。它唯一依赖的是引发异常的外部库。如果您可以选择,作为流控制的异常将始终比返回值慢,并且不会增加任何内容。通用并不能使它成为一个好的模式。尽管如此,我确实看到了很多证据表明它是常用的。是的,不幸的是,文档并没有给出任何用例的提示。谢谢你的回答。根据我的理解,你对拒绝的描述可能是不正确的,如果你所说的“未来”是指“下链”。当错误处理程序运行时,返回值用于实现(而不是拒绝)后续承诺。因此,一旦错误处理程序运行,错误就被视为已处理,执行将在成功端继续进行。换言之,如果您想在链中的连续步骤中处理错误,第一个错误