早日解决/拒绝后是否需要返回?


262

假设我有以下代码。

function divide(numerator, denominator) {
 return new Promise((resolve, reject) => {

  if(denominator === 0){
   reject("Cannot divide by 0");
   return; //superfluous?
  }

  resolve(numerator / denominator);

 });
}

如果我的目的是使用reject提早退房,那么我是否也应该养成return事后立即饮食的习惯?


5
是的,由于运行完成

Answers:


370

return目的是拒绝后终止函数的执行,防止代码执行后它。

function divide(numerator, denominator) {
  return new Promise((resolve, reject) => {

    if (denominator === 0) {
      reject("Cannot divide by 0");
      return; // The function execution ends here 
    }

    resolve(numerator / denominator);
  });
}

在这种情况下,它会阻止resolve(numerator / denominator);执行,这不是严格需要的。但是,仍然最好终止执行以防止将来可能出现陷阱。另外,防止不必要地运行代码是一种很好的做法。

背景

一个承诺可以处于以下三种状态之一:

  1. 待定-初始状态。从待定状态,我们可以移至其他状态之一
  2. 完成-成功运作
  3. 拒绝-操作失败

当一个诺言被兑现或被拒绝时,它会无限期地保持这种状态(结算)。因此,拒绝履行承诺或履行拒绝承诺将没有效果。

此示例代码段显示,尽管诺言在被拒绝后就已实现,但它仍然被拒绝。

function divide(numerator, denominator) {
  return new Promise((resolve, reject) => {
    if (denominator === 0) {
      reject("Cannot divide by 0");
    }

    resolve(numerator / denominator);
  });
}

divide(5,0)
  .then((result) => console.log('result: ', result))
  .catch((error) => console.log('error: ', error));

那么,为什么我们需要返回?

尽管我们无法更改已结算的承诺状态,但是拒绝或解决不会停止其余函数的执行。该函数可能包含会产生混乱结果的代码。例如:

function divide(numerator, denominator) {
  return new Promise((resolve, reject) => {
    if (denominator === 0) {
      reject("Cannot divide by 0");
    }
    
    console.log('operation succeeded');

    resolve(numerator / denominator);
  });
}

divide(5, 0)
  .then((result) => console.log('result: ', result))
  .catch((error) => console.log('error: ', error));

即使函数现在不包含此类代码,这也会造成将来的陷阱。将来的重构可能会忽略以下事实:承诺被拒绝后,代码仍在执行,并且将很难调试。

解析/拒绝后停止执行:

这是标准的JS控制流程内容。

  • resolve/ 之后返回reject

  • resolve/ 返回reject-由于忽略了回调的返回值,我们可以通过返回reject / resolve语句保存一行:

  • 使用if / else块:

我更喜欢使用其中一个return选项,因为代码比较扁平。


28
值得一提的是,无论是否存在,代码实际上都不会有所不同,return因为一旦设置了承诺状态,就无法更改它,因此调用resolve()之后的调用reject()除了使用几个额外的CPU周期外不会做任何事情。我本人return将从代码的清洁度和效率的角度使用正义,但是在此特定示例中并不需要。
jfriend00 2015年

1
尝试使用Promise.try(() => { })而不是新的Promise,并避免使用解析/拒绝调用。相反,您可以写return denominator === 0 ? throw 'Cannot divide by zero' : numerator / denominator; 我用Promise.try它来启动一个Promise并消除包装在try / catch块中的有问题的promise。
kingdango

2
很高兴知道,我喜欢这种模式。然而,在这个时候Promise.try是0期的建议,所以你只能用一个使用垫片或通过使用一个承诺库,如蓝鸟或Q.
大利德罗里

6
@ jfriend00显然,在此简单示例中,代码的行为不会有所不同。但是,如果您之后编写了代码,reject那会花费很多钱,例如连接到数据库或API端点,该怎么办?这一切都是不必要的,并且会花费金钱和资源,特别是例如,如果您要连接到类似AWS数据库或API Gateway终端的对象。在那种情况下,您肯定会使用return来避免执行不必要的代码。
杰克·威尔逊

3
@JakeWilson-当然,那只是Javascript中的正常代码流,与承诺完全无关。如果您已经完成函数的处理并且不想在当前代码路径中执行更多代码,请插入return
jfriend00

37

一个常见的习惯用法(可能是也可能不是您的一杯茶)是将return与结合使用reject,以同时拒绝Promise并退出功能,以便resolve不执行功能的其余部分(包括)。如果您喜欢这种样式,则可以使您的代码更加紧凑。

function divide(numerator, denominator) {
  return new Promise((resolve, reject) => {
    if (denominator === 0) return reject("Cannot divide by 0");
                           ^^^^^^^^^^^^^^
    resolve(numerator / denominator);
  });
}

因为无极构造方法不具有任何返回值,并且在任何情况下也能正常工作resolvereject任何回报。

可以将相同的习惯用法与另一个答案中显示的回调样式一起使用:

function divide(nom, denom, cb){
  if(denom === 0) return cb(Error("Cannot divide by zero"));
                  ^^^^^^^^^
  cb(null, nom / denom);
} 

再次,这很好用,因为调用divide者不希望它返回任何东西,也不对返回值做任何事情。


6
我不喜欢这个。这给出了一个概念,即您正在返回实际上不是的东西。您正在调用拒绝函数,然后使用return结束函数执行。将它们分开放置,您所做的只会使人们感到困惑。代码可读性为王。
K-SO的毒性在增加。

7
@KarlMorrison您实际上正在返回“某物”,这是一个被拒绝的承诺。我认为您所谈论的“概念”非常个人化。恢复reject身份没有错
Frondor '18

5
@Frondor我不认为您理解我写的内容。当然,您和我都理解这一点,当在同一行返回拒绝时,什么也不会发生。但是对于那些不习惯JavaScript进入项目的开发人员呢?这类编程会降低此类人员的可读性。今天的JavaScript生态系统已经很混乱,人们传播这种做法只会使情况变得更糟。这是不好的做法。
K-SO的毒性在增加。

1
@KarlMorrison个人观点!=不良做法。这可能会帮助新的Javascript开发人员了解返回结果。
Toby Caulk

1
@TobyCaulk如果人们需要学习回报是什么,那么他们不应该在Promises上玩,他们应该在学习基本的编程。
K-SO的毒性在增加。

10

从技术上讲,这里不需要1-因为Promise可以排他或仅一次解决拒绝。第一个Promise结局获胜,随后的每个结果都将被忽略。这是不同的节点样式的回调。

话虽这么说,优良作法是确保在实际可行的情况下(在这种情况下)确实要正确调用一个,因为没有进一步的异步/延迟处理。“尽早返回”的决定与完成其工作后结束任何功能没有什么不同 -继续进行无关或不必要的处理。

在适当的时间返回(或以其他方式使用条件语句以避免执行“其他”情况)减少了意外地在无效状态下运行代码或执行不良副作用的机会;因此,它使代码不太容易“意外中断”。


1这个技术上的答案还取决于这样一个事实,在这种情况下,“返回”之后的代码(如果应省略)将不会产生副作用。JavaScript将愉快地除以零并返回+ Infinity / -Infinity或NaN。


好的脚注!
HankCa

9

如果您在解决/拒绝后不“返回”,则在您打算停止它之后,可能会发生坏事(例如页面重定向)。资料来源:我遇到了这个问题。


6
范例为+1。我遇到了一个问题,我的程序将执行100多个无效的数据库查询,而我不知道为什么。事实证明,拒绝后我没有“返回”。这是一个小错误,但我学到了教训。
AdamInTheOculus'Mar 23'23

8

Ori的答案已经说明,没有必要 return但这是一个好习惯。请注意,promise构造函数是安全抛出的,因此它将忽略在路径中稍后传递的抛出异常,从本质上讲,您有一些不容易观察到的副作用。

请注意,returninging在回调中也很常见:

function divide(nom, denom, cb){
     if(denom === 0){
         cb(Error("Cannot divide by zero");
         return; // unlike with promises, missing the return here is a mistake
     }
     cb(null, nom / denom); // this will divide by zero. Since it's a callback.
} 

因此,尽管在promises中是一种很好的做法,但回调是必需的。有关代码的一些注意事项:

  • 您的用例是假设的,实际上不要在同步操作中使用promise。
  • promise构造函数忽略返回值。如果您返回一个未定义的值,某些库将发出警告,以警告您不要返回该值。大多数都不是那么聪明。
  • promise构造函数是安全抛出的,它将异常转换为拒绝,但是正如其他人指出的那样-promise可以解析一次。

4

在许多情况下,可以分别验证参数并立即通过Promise.reject(reason)返回被拒绝的promise 。

function divide2(numerator, denominator) {
  if (denominator === 0) {
    return Promise.reject("Cannot divide by 0");
  }
  
  return new Promise((resolve, reject) => {
    resolve(numerator / denominator);
  });
}


divide2(4, 0).then((result) => console.log(result), (error) => console.log(error));

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.