我是否需要在早期解决/拒绝后返回?

时间:2015-09-12 06:45:49

标签: javascript promise es6-promise

假设我有以下代码。

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

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

  resolve(numerator / denominator);

 });
}

如果我的目标是使用reject提前退出,我是否应该立即养成return之后的习惯?

6 个答案:

答案 0 :(得分:261)

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. 拒绝 - 操作失败
  4. 当承诺得到履行或拒绝时,它将无限期地(定居)保持在这种状态。因此,拒绝履行承诺或履行被拒绝的承诺将不起作用。

    此示例代码段显示虽然承诺在被拒绝后已履行,但仍未被拒绝。

    
    
    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
    • 之后返回

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

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

    
    
    function divide(numerator, denominator) {
      return new Promise((resolve, reject) => {
        if (denominator === 0) {
          return 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));
    
    
    

    • 使用if / else块:

    
    
    function divide(numerator, denominator) {
      return new Promise((resolve, reject) => {
        if (denominator === 0) {
          reject("Cannot divide by 0");
        } else {
          console.log('operation succeeded');
          resolve(numerator / denominator);
        }
      });
    }
    
    divide(5, 0)
      .then((result) => console.log('result: ', result))
      .catch((error) => console.log('error: ', error));
    
    
    

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

答案 1 :(得分:28)

一个常见的习语,可能是或不是你的一杯茶,是将symfony new myblog lts cd /home/debian8/myblog php app/console server:run -vvv [2016-03-19 15:14:11] php.DEBUG: fsockopen(): unable to connect to 127.0.0.1:8000 (Connection refused) {"type":2,"file":"/home/debian8/myblog/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Command/ServerCommand.php","line":59,"level":28928} [OK] Server running on http://127.0.0.1:8000 // Quit the server with CONTROL-C. RUN '/usr/bin/php5' '-S' '127.0.0.1:8000' '/home/debian8/myblog/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/config/router_dev.php' ERR [Sat Mar 19 15:14:36 2016] 127.0.0.1:36496 [200]: / ERR [Sat Mar 19 15:14:37 2016] 127.0.0.1:36497 [200]: /_wdt/cf9a53 ERR [Sat Mar 19 15:14:37 2016] 127.0.0.1:36498 [200]: /favicon.ico ERR [Sat Mar 19 15:14:37 2016] 127.0.0.1:36499 [200]: /favicon.ico ERR return结合起来,同时拒绝承诺并从函数中退出,以便剩下的不执行包括reject的功能。如果您喜欢这种风格,它可以使您的代码更紧凑。

resolve

这很好用,因为Promise构造函数不对任何返回值执行任何操作,并且在任何情况下function divide(numerator, denominator) { return new Promise((resolve, reject) => { if (denominator === 0) return reject("Cannot divide by 0"); ^^^^^^^^^^^^^^ resolve(numerator / denominator); }); } resolve都不返回任何内容。

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

reject

同样,这很好用,因为调用function divide(nom, denom, cb){ if(denom === 0) return cb(Error("Cannot divide by zero")); ^^^^^^^^^ cb(null, nom / denom); } 的人不会指望它返回任何内容,也不会对返回值做任何事情。

答案 2 :(得分:9)

技术上这里不需要 1 - 因为承诺可以解决被拒绝,完全和只有一次。第一个Promise结果获胜,每个后续结果都被忽略。这与节点式回调不同

据说这是良好的清洁实践,以确保在实际操作中确切地调用一个,并且实际上在这种情况下,因为没有进一步的异步/延迟处理。 “早退”的决定与其工作完成时结束任何功能 - 与继续无关或不必要的处理相比没有什么不同。

在适当的时间返回(或以其他方式使用条件来避免执行“其他”情况)减少了在无效状态下意外运行代码或执行不必要的副作用的机会;因此,它使代码不易出现“意外破坏”。

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

答案 3 :(得分:7)

Ori的答案已经解释说return没有必要,但这是一个好习惯。请注意,promise构造函数是throw安全的,因此它将忽略稍后在路径中传递的抛出异常,基本上你有副作用,你不能轻易观察。

请注意,早期return在回调中也很常见:

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构造函数是throw安全的,它会将异常转换为拒绝,但正如其他人所指出的那样 - promise会解析一次。

答案 4 :(得分:7)

如果您在解决/拒绝后没有“返回”,那么在您意图停止之后,可能会发生错误的事情(例如页面重定向)。资料来源:我碰到了这个。

答案 5 :(得分:2)

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

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