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

sam*_*sam 211 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);

 });
}
Run Code Online (Sandbox Code Playgroud)

如果我的目的是reject尽早退出,那么我是否应该return立即养成习惯?

Ori*_*ori 298

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);
  });
}
Run Code Online (Sandbox Code Playgroud)

在这种情况下,它会阻止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));
Run Code Online (Sandbox Code Playgroud)

那么为什么我们需要回归呢?

虽然我们无法更改已结算的承诺状态,但拒绝或解决将不会停止执行其余功能.该函数可能包含会产生令人困惑的结果的代码.例如:

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));
Run Code Online (Sandbox Code Playgroud)

即使该函数现在不包含此类代码,也会产生未来可能的陷阱.未来的重构可能会忽略在拒绝承诺后代码仍然执行的事实,并且很难调试.

解决/拒绝后停止执行:

这是标准的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));
Run Code Online (Sandbox Code Playgroud)

  • 返回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));
Run Code Online (Sandbox Code Playgroud)

  • 使用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));
Run Code Online (Sandbox Code Playgroud)

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

  • 值得注意的是,如果`return`存在与否,代码实际上不会有不同的行为,因为一旦设置了一个promise状态,就不能改变它,所以在调用`reject()`之后调用`resolve()`赢了除了使用一些额外的CPU周期之外什么都不做.我本人将仅从代码清洁度和效率的角度使用"return",但在这个具体示例中并不需要. (24认同)
  • @ jfriend00显然在这个简单的例子中,代码的行为不会有所不同.但是,如果您在"拒绝"之后有代码执行昂贵的操作(例如连接到数据库或API端点),该怎么办?这将是不必要的,并且会花费您的资金和资源,特别是例如,如果您要连接到AWS数据库或API Gateway端点之类的东西.在这种情况下,您肯定会使用返回,以避免执行不必要的代码. (5认同)
  • @JakeWilson - 当然,这只是Javascript中的正常代码流,与promises完全无关.如果您已完成处理该函数并且不希望在当前代码路径中执行任何其他代码,则插入`return`. (3认同)
  • 尝试使用 Promise.try(() => { })` 而不是 new Promise 并避免使用解析/拒绝调用。相反,你可以只写`return denominator === 0 ? throw '不能除以零' : 分子/分母;` 我使用 `Promise.try` 作为启动 Promise 的方法,并消除包含在有问题的 try/catch 块中的 Promise。 (2认同)
  • 很高兴知道,我喜欢这种模式.但是,此时[Promise.try](https://github.com/ljharb/proposal-promise-try)是第0阶段的建议,所以你只能使用[shim](https:// www) .npmjs.com/package/es6-promise-try)或使用promise bird,如bluebird或Q. (2认同)

小智 32

一个常见的习语,可能是也可能不是你的一杯茶,是将the return与the reject,,,同时拒绝承诺并退出函数,以便包括the的函数的其余部分resolve不被执行.如果您喜欢这种风格,它可以使您的代码更紧凑.

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

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

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

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

同样,这很好用,因为调用的人divide不会期望它返回任何东西而且不会对返回值做任何事情.

  • @KarlMorrison你实际上正在回归"某事",这是一个被拒绝的承诺.我认为你所谈论的"概念"非常个人化.返回"拒绝"状态没有错 (6认同)
  • 我不喜欢这个.这给出了一个概念,即你正在返回一些你实际上没有的东西.您正在调用reject函数,然后使用return来结束函数执行.把它们放在不同的界限上,你所做的只会让人迷惑.代码可读性是王道. (5认同)
  • @Frondor我不认为您理解我写的内容。当然,您和我都理解这一点,当在同一行上返回拒绝时,什么也不会发生。但是那些不习惯JavaScript进入项目的开发人员呢?此类编程会降低此类人员的可读性。今天的JavaScript生态系统已经很混乱,人们传播这种做法只会使情况变得更糟。这是不好的做法。 (4认同)
  • @TobyCaulk 如果人们需要了解 return 的作用,那么他们不应该玩 Promises,他们应该学习基本的编程。 (3认同)
  • @KarlMorrison 个人意见!= 不好的做法。它可能会帮助新的 Javascript 开发人员了解返回的内容。 (2认同)

use*_*740 10

技术上在这里不需要它1 -因为承诺可以解决或者拒绝,仅仅只有一次.第一个Promise结果获胜,每个后续结果都被忽略.这是不同的节点样式的回调.

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

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


1这个技术上的答案也取决于这样一个事实,即在这种情况下,"返回"之后的代码,如果省略,将不会产生副作用.JavaScript将很乐意除以零并返回+ Infinity/-Infinity或NaN.


Ben*_*n H 9

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

  • 示例为+1.我有一个问题,我的程序将进行100多个无效的数据库查询,我无法弄清楚原因.原来我拒绝后没有"回归".这是一个小错误,但我吸取了教训. (6认同)

Ben*_*aum 8

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.
} 
Run Code Online (Sandbox Code Playgroud)

因此,虽然它是承诺的良好实践,但它需要回调.关于代码的一些注意事项:

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


Dor*_*rad 6

在许多情况下,可以单独验证参数并立即使用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));
Run Code Online (Sandbox Code Playgroud)