wake-up-neo.com

Muss ich nach einer vorzeitigen Lösung / Ablehnung zurückkehren?

Angenommen, ich habe den folgenden Code.

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

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

  resolve(numerator / denominator);

 });
}

Wenn mein Ziel darin besteht, reject zu verwenden, um vorzeitig zu beenden, sollte ich mich dann auch daran gewöhnen, unmittelbar danach returnzu tun?

192
sam

Der Zweck von return besteht darin, die Ausführung der Funktion nach der Zurückweisung zu beenden und die Ausführung des Codes danach zu verhindern.

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

In diesem Fall wird die Ausführung von resolve(numerator / denominator); verhindert, was nicht unbedingt erforderlich ist. Es ist jedoch immer noch vorzuziehen, die Ausführung zu beenden, um eine mögliche Überfüllung in der Zukunft zu verhindern. Darüber hinaus empfiehlt es sich, die unnötige Ausführung von Code zu verhindern.

Hintergrund

Ein Versprechen kann in einem von 3 Zuständen sein:

  1. ausstehend - Ausgangszustand. Von anstehend können wir in einen der anderen Staaten wechseln
  2. erfüllt - erfolgreiche Operation
  3. abgelehnt - Fehlgeschlagene Operation

Wenn ein Versprechen erfüllt oder abgelehnt wird, bleibt es auf unbestimmte Zeit in diesem Zustand (erledigt). Das Ablehnen eines erfüllten Versprechens oder das Erfüllen eines abgelehnten Versprechens hat keine Auswirkung.

Dieses Beispiel-Snippet zeigt, dass das Versprechen zwar erfüllt wurde, aber abgelehnt wurde.

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

Warum müssen wir also zurückkehren?

Obwohl wir einen festgelegten Versprechungsstatus nicht ändern können, wird die Ausführung des Restes der Funktion durch Ablehnen oder Auflösen nicht gestoppt. Die Funktion kann Code enthalten, der zu verwirrenden Ergebnissen führt. Zum Beispiel:

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

Selbst wenn die Funktion derzeit keinen solchen Code enthält, entsteht eine mögliche zukünftige Falle. Ein zukünftiger Refactor ignoriert möglicherweise die Tatsache, dass der Code nach Ablehnung des Versprechens immer noch ausgeführt wird und sich nur schwer debuggen lässt.

Beenden der Ausführung nach Erledigung/Zurückweisung:

Dies ist die Standardeinstellung für JS-Kontrollabläufe.

  • Rückkehr nach dem 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));
  • Rückgabe mit dem resolve/reject - Da der Rückgabewert des Rückrufs ignoriert wird, können wir eine Zeile speichern, indem wir die Anweisung reject/resolve zurückgeben:
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));
  • Verwenden eines if/else-Blocks:
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));

Ich bevorzuge die Verwendung einer der Optionen return, da der Code flacher ist.

274
Ori Drori

Eine gebräuchliche Redewendung, die Ihre Tasse Tee sein kann oder nicht, besteht darin, das return mit dem reject zu kombinieren, um gleichzeitig das Versprechen abzulehnen und die Funktion zu verlassen, so dass der Rest von Die Funktion mit dem resolve wird nicht ausgeführt. Wenn Sie diesen Stil mögen, kann er Ihren Code ein bisschen kompakter machen.

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

Dies funktioniert einwandfrei, da der Promise-Konstruktor mit keinem Rückgabewert etwas anfängt und in jedem Fall resolve und reject nichts zurückgeben.

Dieselbe Redewendung kann mit dem in einer anderen Antwort gezeigten Rückrufstil verwendet werden:

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

Auch dies funktioniert einwandfrei, da die Person, die divide aufruft, keine Rückgabe erwartet und mit dem Rückgabewert nichts unternimmt.

29
user663031

Technisch wird es hier nicht benötigt1 - weil ein Versprechen gelöst oder abgelehnt werden kann, ausschließlich und nur einmal . Das erste Promise-Ergebnis gewinnt und jedes nachfolgende Ergebnis wird ignoriert. Dies unterscheidet sich von Callbacks im Node-Stil .

Davon abgesehen ist es eine gute saubere Praxis , sicherzustellen, dass genau eine aufgerufen wird, wenn dies praktikabel ist, und zwar in diesem Fall, da keine weitere asynchrone/verzögerte wird bearbeitet. Die Entscheidung, "vorzeitig zurückzukehren" ist nicht anders als das Beenden einer Funktion, wenn ihre Arbeit abgeschlossen ist - vs. unnötige Bearbeitung.

Wenn Sie zum richtigen Zeitpunkt zurückkehren (oder auf andere Weise Bedingungen verwenden, um die Ausführung des "anderen" Falls zu vermeiden), wird die Wahrscheinlichkeit verringert, dass versehentlich Code in einem ungültigen Zustand ausgeführt wird oder unerwünschte Nebenwirkungen auftreten. und als solches macht es Code weniger anfällig für "unerwartetes Brechen".


1 Diese technisch Antwort hängt auch davon ab, dass in diesem Fall der Code nach dem "return", sollte es weggelassen werden, wird es keine Nebenwirkung geben. JavaScript wird glücklich durch Null dividieren und entweder + Infinity/-Infinity oder NaN zurückgeben.

9
user2864740

Wenn Sie nach einer Lösung/Zurückweisung nicht "zurückkehren", kann es zu schlechten Ergebnissen (z. B. einer Seitenumleitung) kommen, nachdem Sie beabsichtigt haben, dass diese beendet werden soll. Quelle: Ich bin darauf gestoßen.

7
Benjamin H

Die Antwort von Ori erklärt bereits, dass es nicht notwendig ist, return, aber es ist eine gute Praxis. Beachten Sie, dass der Versprechen-Konstruktor sicher ist, so dass er ausgelöste Ausnahmen, die später im Pfad übergeben werden, ignoriert. Im Grunde haben Sie Nebenwirkungen, die Sie nicht leicht beobachten können.

Beachten Sie, dass das frühe returning auch in Rückrufen häufig vorkommt:

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

Also, während es in Versprechungen eine gute Praxis ist, ist es erforderlich mit Rückrufen. Einige Anmerkungen zu Ihrem Code:

  • Ihr Anwendungsfall ist hypothetisch. Verwenden Sie keine Versprechen mit synchronen Aktionen.
  • Der Versprechen-Konstruktor ignoriert gibt Werte zurück. Einige Bibliotheken warnen, wenn Sie einen nicht definierten Wert zurückgeben, um Sie vor dem Fehler zu warnen, dorthin zurückzukehren. Die meisten sind nicht so schlau.
  • Der Versprechen-Konstruktor ist werfsicher, er wandelt Ausnahmen in Ablehnungen um, aber wie andere darauf hingewiesen haben - ein Versprechen wird einmal ausgeführt.
7

In vielen Fällen ist es möglich, Parameter separat zu validieren und eine abgelehnte Zusage sofort mit Promise.reject (reason) zurückzugeben.

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