早期解決/却下後に戻る必要がありますか?


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その後すぐに食事をする習慣に入る必要がありますか?


回答:


371

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);、厳密には必要ありませんが、の実行が妨げられます。ただし、今後トラップが発生しないように、実行を終了することをお勧めします。さらに、コードを不必要に実行しないようにすることをお勧めします。

バックグラウンド

promiseは次の3つの状態のいずれかになります。

  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コードはよりフラットなので、私はオプションの1つを使用することを好みます。


28
return存在するかどうかに関係なく、コードは実際には異なる動作をしないことに注意する価値があります。いったんpromise状態が設定されると変更できないため、呼び出しresolve()後に呼び出しreject()ても、追加のCPUサイクルをいくつか使用する以外は何もしません。私自身、returnコードの清潔さと効率の観点からだけを使用しますが、この特定の例では必要ありません。
jfriend00

1
Promise.try(() => { })新しいPromiseの代わりに使用してみて、解決/拒否呼び出しの使用を避けます。代わりに、Promiseを開始する手段としてreturn denominator === 0 ? throw 'Cannot divide by zero' : numerator / denominator; 使用するだけでPromise.tryなく、問題のあるtry / catchブロックにラップされているpromiseを削除することもできます。
kingdango 2016

2
知っておくと良いですし、パターンが好きです。しかし、この時点でPromise.tryは、ステージ0の提案は、あなただけでそれを使用できるように、あるシムや、青い鳥やQ.として約束ライブラリを使用して
オリDrori

6
@ jfriend00明らかにこの単純な例では、コードは異なる動作をしません。しかし、その後rejectにデータベースやAPIエンドポイントへの接続などの高額なコードを実行するとどうなるでしょうか。特にAWSデータベースやAPI Gatewayエンドポイントなどに接続している場合は特に、すべて不要であり、費用とリソースがかかります。その場合は、不要なコードが実行されないようにするために、必ずreturnを使用します。
Jake Wilson

3
@JakeWilson-もちろん、これはJavascriptの通常のコードフローであり、promiseとはまったく関係ありません。関数の処理が完了し、現在のコードパスでこれ以上コードを実行したくない場合は、を挿入しreturnます。
jfriend00

37

またはお茶のあなたのカップではないかもしれない可能性のある一般的なイディオムは、組み合わせることであるreturnとのreject機能の残りの部分を含むように、同時に機能から約束して終了を拒否し、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

5
@Frondor私が書いた内容が理解できないと思います。もちろん、あなたと私はこれを理解しています。同じ行に拒否を返すときは何も起こりません。しかし、プロジェクトに入るJavaScriptにそれほど慣れていない開発者はどうでしょうか?このタイプのプログラミングは、そのような人々の可読性を低下させます。今日のJavaScriptエコシステムはめちゃくちゃで、このタイプの慣行を広める人々はそれを悪化させるだけです。これは悪い習慣です。
K-SOの毒性が高まっています。

1
@KarlMorrison個人的な意見!=悪い習慣。これはおそらく、新しいJavascript開発者がリターンで何が起こっているのかを理解するのに役立ちます。
Toby Caulk

1
@TobyCaulk人々がリターンが何をするかを学ぶ必要があるなら、彼らはプロミスで遊んではいけません、彼らは基本的なプログラミングを学ぶべきです。
K-SOの毒性が高まっています。

10

技術的にはここでは必要ありません1 -Promiseは排他的かつ一度だけ解決または拒否できるためです。最初のPromiseの結果が優先され、その後の結果はすべて無視されます。これは、ノードスタイルのコールバックとは異なります。

ことで、あると良いクリーンな練習それ以上の非同期/繰延処理が存在しないので、正確に一つが、この場合には実際に呼び出され、時に実用的、およびされていることを確認します。「早期に戻る」という決定は、機能が完了したときに機能を終了することと、無関係または不要な処理を継続することとの違いはありません

適切なタイミングで戻る(または条件式を使用して「その他の」ケースの実行を回避する)ことにより、コードが無効な状態で誤って実行されたり、不要な副作用が発生したりする可能性が低くなります。そのため、コードが「予期せず壊れる」可能性が低くなります。


1この技術的な答えは、この場合、「リターン」の後のコードが省略された場合に副作用が発生しないという事実にも依存します。JavaScriptは喜んでゼロで除算し、+ Infinity / -InfinityまたはNaNを返します。


いい脚注!!
HankCa

9

解決/拒否の後で「戻る」ことを行わない場合、停止を意図した後で、ページリダイレクトなどの問題が発生する可能性があります。出典:私はこれに遭遇しました。


6
例では+1。私のプログラムが100以上の無効なデータベースクエリを作成するという問題があり、その理由を理解できませんでした。却下後、「返却」しなかったことが判明しました。それは小さな間違いですが、私は自分の教訓を学びました。
AdamInTheOculus 2017年

8

Oriの回答はすでに必要ではないことを説明していますreturnが、それは良い習慣です。promiseコンストラクターはスローセーフであるため、パスで後で渡されるスローされた例外を無視します。基本的に、簡単に確認できない副作用があります。

ことに注意してくださいreturn早期INGのはコールバックにも非常に一般的です。

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

したがって、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));

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.