Was passiert, wenn Sie ein Versprechen nicht lösen oder ablehnen?

73

Ich habe ein Szenario, in dem ich ein Versprechen zurückgebe. Das Versprechen wird im Wesentlichen durch eine Ajax-Anfrage ausgelöst.

Beim Ablehnen des Versprechens wird ein Fehlerdialog angezeigt, in dem ein Serverfehler vorliegt.

Wenn der Antwortcode 401 lautet, möchte ich das Versprechen weder auflösen noch ablehnen (da der Fehlerdialog bereits angezeigt wird). Ich möchte einfach zur Anmeldeseite weiterleiten.

Mein Code sieht ungefähr so ​​aus:

function makeRequest(ur, params) {
  return new Promise(function (resolve, reject) {
    fetch(url, params).then((response) => {
      let status = response.status;    
      if (status >= 200 && status < 300) {
        response.json().then((data) => {
          resolve(data);
        });
      } else {
        if (status === 401) {
          redirectToLoginPage();
        } else {
          response.json().then((error) => {
            if (!error.message) {
              error.message = constants.SERVER_ERROR;
            }
            reject({ status, error });
          });
        }
      }
    });
  });
}

Wie Sie sehen können, leite ich bei einem Status von 401 zur Anmeldeseite weiter. Das Versprechen wird weder gelöst noch abgelehnt.

Ist dieser Code in Ordnung oder gibt es einen besseren Weg, dies zu erreichen?

Aniket
quelle

Antworten:

113

Ein Versprechen ist nur ein Objekt mit Eigenschaften in Javascript. Es gibt keine Magie. Wenn Sie also ein Versprechen nicht lösen oder ablehnen, ändert sich der Status nie von "ausstehend" zu etwas anderem. Dies verursacht in Javascript kein grundlegendes Problem, da ein Versprechen nur ein reguläres Javascript-Objekt ist. Das Versprechen wird weiterhin Müll gesammelt (auch wenn es noch aussteht), wenn kein Code einen Verweis auf das Versprechen enthält.

Die eigentliche Konsequenz ist hier, was bedeutet das für den Verbraucher des Versprechens, wenn sich sein Zustand nie ändert? Alle .then()oder .catch()Listener, die Übergänge auflösen oder ablehnen, werden niemals angerufen. Der meiste Code, der Versprechen verwendet, erwartet, dass sie irgendwann in der Zukunft aufgelöst oder abgelehnt werden (deshalb werden Versprechen an erster Stelle verwendet). Wenn dies nicht der Fall ist, kann dieser Code seine Arbeit im Allgemeinen nie beenden.

Es ist möglich, dass Sie einen anderen Code haben, der die Arbeit für diese Aufgabe beendet, und das Versprechen wird einfach aufgegeben, ohne jemals etwas zu tun. Es gibt kein internes Problem in Javascript, wenn Sie es so machen, aber es ist nicht so, wie Versprechen funktionieren sollen, und es ist im Allgemeinen nicht so, wie der Konsument von Versprechen erwartet, dass sie funktionieren.

Wie Sie sehen können, wenn der Status 401 lautet, leite ich zur Anmeldeseite weiter. Versprechen wird weder gelöst noch abgelehnt.

Ist dieser Code in Ordnung? Oder gibt es einen besseren Weg, dies zu erreichen?

In diesem speziellen Fall ist alles in Ordnung und eine Weiterleitung ist ein etwas besonderer und einzigartiger Fall. Durch eine Weiterleitung auf eine neue Browserseite wird der aktuelle Seitenstatus (einschließlich des gesamten Javascript-Status) vollständig gelöscht. Es ist also vollkommen in Ordnung, eine Verknüpfung mit der Umleitung zu erstellen und andere Dinge einfach ungelöst zu lassen. Das System initialisiert Ihren Javascript-Status vollständig neu, wenn die neue Seite geladen wird, sodass alle noch ausstehenden Versprechen bereinigt werden.

jfriend00
quelle
4
Wie Sie bereits erwähnt haben, wird durch eine Weiterleitung auf eine neue Browserseite der aktuelle Seitenstatus (einschließlich des gesamten Javascript-Status) vollständig gelöscht. Aber was passiert, wenn ich in einer App mit nur einer Seite umleitung? Ich benutze ReactJS. Es wird also keine neue Browserseite geben, nur eine andere Ansicht. Ob es in diesem Fall vollkommen in Ordnung ist, eine Verknüpfung mit der Umleitung zu verwenden und andere Dinge einfach ungelöst zu lassen.
Aniket
@Aniket - Dann ist das ein anderer Fall, den Ihre Frage nicht beschreibt. Sie müssen sicherstellen, dass die Dinge ordnungsgemäß bereinigt werden, damit keine Speicherlecks auftreten. Ich habe keine Ahnung, ob es ein Problem damit geben würde oder nicht. Bitte geben Sie diese Art von Informationen beim nächsten Mal in Ihre Frage ein.
jfriend00
Stellen Sie sicher , dass keine Verweise auf den rejectund resolveFunktionen, und nicht nur stoppen , das Versprechen zu verweisen. Dann sollte GC eingreifen. Andernfalls, wenn Ihr Code beispielsweise immer noch referenziert resolve, muss das Versprechen bestehen bleiben, falls Ihr Code jemals aufruft resolve(). Hier ist ein Beispiel für eine Web-API, die Versprechen
verlieren
Für Ihr Single-Page-App-Szenario bei der Verarbeitung von 401 können Sie einfach "zurückkehren", wie von @CodingIntrigue unten erwähnt. Sicher, Sie müssen das if (typeof json === "undefined") überprüfen, aber die Aufrufer von makeRequest sollten prüfen, ob sie trotzdem gute Daten haben. Anrufer sollten niemals blind darauf vertrauen, dass sie gute Daten haben. Für einen 401 würde ich das Versprechen nicht ablehnen. Ein abgelehntes Versprechen bedeutet für mich, dass ein Fehler protokolliert werden sollte. Dies ist nicht wirklich ein Fehler, sondern eine einfache Weiterleitung.
RayLoveless
6

Ich denke, das "Was passiert, wenn wir die Ablehnung nicht lösen" wurde gut beantwortet - Sie haben die Wahl, ob Sie ein .thenoder ein hinzufügen möchten .catch.

Allerdings ist dieser Code in Ordnung? Oder gibt es einen besseren Weg , dies zu erreichen . Ich würde sagen, es gibt zwei Dinge:

new PromiseWenn Sie ein Versprechen einpacken, wenn es nicht erforderlich ist und der fetchAnruf fehlschlagen kann , sollten Sie darauf reagieren, damit Ihre aufrufende Methode nicht auf ein Versprechen wartet, das niemals gelöst wird.

Hier ist ein Beispiel (ich denke, dies sollte für Ihre Geschäftslogik funktionieren, nicht 100% sicher):

const constants = {
  SERVER_ERROR: "500 Server Error"
};
function makeRequest(url,params) {
  // fetch already returns a Promise itself
  return fetch(url,params)
        .then((response) => {

            let status = response.status;

            // If status is forbidden, redirect to Login & return nothing,
            // indicating the end of the Promise chain
            if(status === 401) {
              redirectToLoginPage();
              return;
            }
            // If status is success, return a JSON Promise
            if(status >= 200 && status < 300) {
              return response.json();
            }
            // If status is a failure, get the JSON Promise,
            // map the message & status, then Reject the promise
            return response.json()
              .then(json => {
                if (!json.message) {
                    json.message = constants.SERVER_ERROR;
                }
                return Promise.reject({status, error: json.message});
              })
        });
}
// This can now be used as:
makeRequest("http://example", {})
  .then(json => {
    if(typeof json === "undefined") {
      // Redirect request occurred
    }
    console.log("Success:", json);
  })
  .catch(error => {
    console.log("Error:", error.status, error.message);
  })

Im Gegensatz dazu rufen Sie Ihren Code auf mit:

makeRequest("http://example", {})
  .then(info => console.log("info", info))
  .catch(err => console.log("error", err));

Protokolliert nichts, da der Aufruf von http://examplefehlschlägt, der catchHandler jedoch niemals ausgeführt wird.

CodingIntrigue
quelle
1
Ich wollte nur vermeiden, ob (typeof json === "undefined") {// Umleitungsanforderung aufgetreten ist} Der Grund dafür, dass makeRequest aufgerufen wird, wird von vielen anderen Funktionen aufgerufen, und ich muss diese Prüfung in jeder Funktion durchführen, die sie aufruft.
Aniket
Ich bin nicht sicher, ob ich Ihren Codebeispielen folge. Also wird für beide Beispiele der makeRequest (..). Catch nicht aufgerufen? ist das richtig? So liest es mir vor. Anscheinend brauchen Sie einen Haken in Ihrem ursprünglichen Abruf und lassen ihn ein abgelehntes Versprechen zurückgeben.
RayLoveless
2

Wie andere sagten, ist es wahr, dass es nicht wirklich ein Problem ist, wenn Sie ein Versprechen nicht lösen / ablehnen. Auf jeden Fall würde ich Ihr Problem etwas anders lösen:

function makeRequest(ur,params) {

    return new Promise(function(resolve,reject) {

        fetch(url,params)
        .then((response) => {

            let status = response.status;

            if (status >= 200 && status < 300) {
                response.json().then((data) => {
                    resolve(data);
                })
            }
            else {
                reject(response);
            }
        })
    });
}

makeRequest().then(function success(data) {
   //...
}, function error(response) {
    if (response.status === 401) {
        redirectToLoginPage();
    }
    else {
        response.json().then((error) => {
            if (!error.message) {
                error.message = constants.SERVER_ERROR;
            }

            //do sth. with error
        });
    } 
});

Das heißt, ich würde jeden schlechten Antwortzustand ablehnen und dies dann in Ihrem error handlervon Ihnen behandeln makeRequest.

Fidel90
quelle
Benötigen Sie für einen Abruf einen .catch-Handler, der ein abgelehntes Versprechen zurückgibt?
RayLoveless
1

Es funktioniert und ist nicht wirklich ein Problem, außer wenn ein Anrufer makeRequesterwartet, dass das Versprechen erfüllt wird. Sie brechen dort also den Vertrag.

Stattdessen können Sie das Versprechen verschieben oder (in diesem Fall) mit Statuscode / Fehler ablehnen.

nullpotent
quelle
0

Die ECMAScript-Spezifikation erklärt den Zweck von Versprechungen und new Promise():

Ein Versprechen ist ein Objekt, das als Platzhalter für die möglichen Ergebnisse einer verzögerten (und möglicherweise asynchronen) Berechnung verwendet wird.

25.6.3.1 Versprechen ( Testamentsvollstrecker )

HINWEIS Das Executor- Argument muss ein Funktionsobjekt sein. Es wird aufgefordert, den Abschluss der möglicherweise zurückgestellten Aktion, die durch dieses Promise-Objekt dargestellt wird, einzuleiten und den Abschluss zu melden.

Sie sollten Versprechen verwenden, um zukünftige Werte zu erhalten. Um Ihren Code präzise und direkt zu halten, sollten Sie außerdem nur Versprechen verwenden, um zukünftige Werte zu erhalten, und keine anderen Dinge tun.

Da Sie auch den Programmsteuerungsfluss (Umleitungslogik) in die "Executor" -Logik Ihres Versprechens gemischt haben, ist Ihr Versprechen nicht länger "ein Platzhalter für die Ergebnisse einer Berechnung". Vielmehr handelt es sich jetzt um ein kleines JavaScript-Programm, das sich als Versprechen tarnt.

Anstatt dieses JavaScript-Programm in a zu verpacken new Promise(), empfehle ich, es einfach wie ein normales JavaScript-Programm zu schreiben:

async function makeRequest(url, params) {
  let response = await fetch(url, params);
  let { status } = response;
  if (status >= 200 && status < 300) {
    let data = await response.json();
    successLogic(data);
  } else if (status === 401) {
    redirectToLoginPage();
  } else {
    let error = await response.json()
    if (!error.message) {
      error.message = constants.SERVER_ERROR;
    }
    errorLogic({ status, error });
  }
}
Jackson
quelle