Warum gibt meine asynchrone Funktion Promise {<pending>} anstelle eines Werts zurück?


127

Mein Code:

let AuthUser = data => {
  return google.login(data.username, data.password).then(token => { return token } )
}

Und wenn ich versuche, so etwas auszuführen:

let userToken = AuthUser(data)
console.log(userToken)

Ich erhalte:

Promise { <pending> }

Aber wieso?

Mein Hauptziel ist es, ein Token, von google.login(data.username, data.password)dem ein Versprechen zurückgegeben wird, in eine Variable zu verwandeln. Und erst dann einige Aktionen durchführen.


1
@ LoïcFaure-Lacroix, siehe diesen Artikel: medium.com/@bluepnume/…
Src

@ LoïcFaure-Lacroix Blick auf getFirstUserFunktion
Src

Was ist damit? Es ist eine Funktion, die ein Versprechen zurückgibt.
Loïc Faure-Lacroix

1
@ LoïcFaure-Lacroix Sie meinen also, auch in diesem Beispiel müssen wir dann verwenden, um auf das Datenversprechen zuzugreifen, das in der Funktion getFirstUser zurückgegeben wird?
Src

In diesem Beispiel besteht die einzige andere Möglichkeit darin, die ES7-Syntax "await" zu verwenden, mit der die Ausführung des aktuellen Kontexts gestoppt werden soll, um auf das Ergebnis des Versprechens zu warten. Wenn Sie den Artikel lesen, werden Sie ihn sehen. Aber da ES7 wahrscheinlich noch nirgendwo unterstützt wird, ja. Das "Dann" ist so ziemlich alles.
Loïc Faure-Lacroix

Antworten:


174

Das Versprechen wird immer ausstehend protokolliert, solange die Ergebnisse noch nicht geklärt sind. Sie müssen .thendas Versprechen aufrufen , um die Ergebnisse unabhängig vom Versprechungsstatus (gelöst oder noch ausstehend) zu erfassen:

let AuthUser = function(data) {
  return google.login(data.username, data.password).then(token => { return token } )
}

let userToken = AuthUser(data)
console.log(userToken) // Promise { <pending> }

userToken.then(function(result) {
   console.log(result) // "Some User token"
})

Warum ist das so?

Versprechen sind nur Vorwärtsrichtung; Sie können sie nur einmal lösen. Der aufgelöste Wert von a Promisewird an seine .thenoder .catchMethoden übergeben.

Einzelheiten

Gemäß der Promises / A + Spezifikation:

Das Versprechenlösungsverfahren ist eine abstrakte Operation, bei der ein Versprechen und ein Wert als Eingabe verwendet werden, die wir als [[Auflösen]] (Versprechen, x) bezeichnen. Wenn x ein dannable ist, versucht es, das Versprechen dazu zu bringen, den Zustand von x anzunehmen, unter der Annahme, dass sich x zumindest etwas wie ein Versprechen verhält. Ansonsten erfüllt es das Versprechen mit dem Wert x.

Diese Behandlung von thenables ermöglicht die Interoperation von Versprechen-Implementierungen, solange sie eine Promises / A + -konforme then-Methode offenlegen. Es ermöglicht Promises / A + -Implementierungen auch, nicht konforme Implementierungen mit angemessenen Methoden zu „assimilieren“.

Diese Spezifikation ist etwas schwer zu analysieren, also lasst es uns zusammenfassen. Die Regel lautet:

Wenn die Funktion im .thenHandler einen Wert zurückgibt, wird der Promisemit diesem Wert aufgelöst. Wenn der Handler einen anderen zurückgibt Promise, wird das Original Promisemit dem aufgelösten Wert der verketteten aufgelöst Promise. Der nächste .thenHandler enthält immer den aufgelösten Wert des verketteten Versprechens, das im vorhergehenden zurückgegeben wurde .then.

Die Art und Weise, wie es tatsächlich funktioniert, wird nachstehend ausführlicher beschrieben:

1. Die Rückgabe der .thenFunktion ist der aufgelöste Wert des Versprechens.

function initPromise() {
  return new Promise(function(res, rej) {
    res("initResolve");
  })
}

initPromise()
  .then(function(result) {
    console.log(result); // "initResolve"
    return "normalReturn";
  })
  .then(function(result) {
    console.log(result); // "normalReturn"
  });

2. Wenn die .thenFunktion a zurückgibt Promise, wird der aufgelöste Wert dieses verketteten Versprechens an Folgendes übergeben .then.

function initPromise() {
  return new Promise(function(res, rej) {
    res("initResolve");
  })
}

initPromise()
  .then(function(result) {
    console.log(result); // "initResolve"
    return new Promise(function(resolve, reject) {
       setTimeout(function() {
          resolve("secondPromise");
       }, 1000)
    })
  })
  .then(function(result) {
    console.log(result); // "secondPromise"
  });

Dein erster funktioniert nicht. Uncaught SyntaxError: Unexpected token .. Der zweite braucht eine Rückkehr fürPromise
Zamil

@zamil Sie müssen die Funktion aufrufen, wie im zweiten Beispiel. Sie können nicht .thenauf eine nicht aufgerufene Funktion. aktualisierte die Antwort
Bamieh

1
Ich setze ein Lesezeichen, damit ich es für immer behalten kann. Ich habe SEHR lange daran gearbeitet, wirklich klare und lesbare Regeln zu finden, wie man tatsächlich Versprechen aufbaut. Ihr Blockquote von Versprechen / A + -Spezifikation ist ein perfektes Beispiel dafür, warum es eine PITA war, Versprechen selbst zu lehren. Es ist auch das EINZIGE Mal, dass setTimeout verwendet wurde, wo es die Lektion selbst nicht verwirrte. Und ausgezeichnete Referenz, danke.
Montag,

21

Ich weiß, dass diese Frage vor 2 Jahren gestellt wurde, aber ich stoße auf dasselbe Problem und die Antwort auf das Problem lautet seit ES6, dass Sie einfach awaitdie Funktionen zurückgeben können, wie zum Beispiel:

let AuthUser = function(data) {
  return google.login(data.username, data.password).then(token => { return token } )
}

let userToken = await AuthUser(data)
console.log(userToken) // your data

3
Sie brauchen das nicht .then(token => return token), das ist nur ein unnötiger Passthrough. Geben Sie einfach den Google-Login-Anruf zurück.
Soviut

Diese Antwort hat nichts mit der Frage zu tun. Das Problem des Originalplakats hat nichts mit ES6 'async / await zu tun. Versprechen gab es, bevor dieser neue syntaktische Zucker in ECMAScript 2017 eingeführt wurde, und sie verwendeten Versprechen "unter der Haube". Siehe MDN auf asynchron / warten .
Try-Catch-Endlich

Bei ES8 / Nodejs werden Fehler ausgegeben, wenn Sie sie awaitaußerhalb einer asynchronen Funktion verwenden. Vielleicht wäre das bessere Beispiel hier, die AuthUserFunktion zu machen async, die dann endet mitreturn await google.login(...);
Jon L.

4

Die thenMethode gibt ein ausstehendes Versprechen zurück, das asynchron durch den Rückgabewert eines im Aufruf an registrierten Ergebnishandlers aufgelöst thenoder durch Auslösen eines Fehlers im aufgerufenen Handler zurückgewiesen werden kann.

Ein Anruf AuthUsermeldet den Benutzer also nicht plötzlich synchron an, sondern gibt ein Versprechen zurück, dessen dann registrierte Handler aufgerufen werden, nachdem die Anmeldung erfolgreich war (oder fehlschlägt). Ich würde vorschlagen, die gesamte Anmeldeverarbeitung durch eine thenKlausel des Anmeldeversprechens auszulösen . EG verwendet benannte Funktionen, um die Abfolge des Flusses hervorzuheben:

let AuthUser = data => {   // just the login promise
  return google.login(data.username, data.password);
};

AuthUser(data).then( processLogin).catch(loginFail);

function processLogin( token) {
      // do logged in stuff:
      // enable, initiate, or do things after login
}
function loginFail( err) {
      console.log("login failed: " + err);
}

1

Siehe den MDN-Abschnitt zu Versprechen. Schauen Sie sich insbesondere den Rückgabetyp von then () an.

Um sich anzumelden, muss der Benutzeragent eine Anfrage an den Server senden und auf eine Antwort warten. Da die Ausführung Ihrer Anwendung während eines Anforderungsrundgangs normalerweise zu einer schlechten Benutzererfahrung führt, verwendet praktisch jede JS-Funktion, die Sie anmeldet (oder eine andere Form der Serverinteraktion ausführt), ein Versprechen oder etwas Ähnliches , um Ergebnisse asynchron zu liefern.

Beachten Sie jetzt auch, dass returnAnweisungen immer im Kontext der Funktion ausgewertet werden, in der sie erscheinen. Wenn Sie also geschrieben haben:

let AuthUser = data => {
  return google
    .login(data.username, data.password)
    .then( token => {
      return token;
    });
};

Die Anweisung return token;bedeutete, dass die anonyme Funktion, an then()die übergeben wird, das Token zurückgeben sollte, nicht dass die AuthUserFunktion sollte. Was AuthUserzurückkommt, ist das Ergebnis eines Anrufs google.login(username, password).then(callback);, der zufällig ein Versprechen ist.

Letztendlich macht Ihr Rückruf token => { return token; }nichts; Stattdessen muss Ihre Eingabe an then()eine Funktion sein, die das Token tatsächlich auf irgendeine Weise verarbeitet.


@Src Ich schrieb meine Antwort, bevor der Fragesteller klarstellte, dass er nach einer Möglichkeit suchte, einen Wert synchron zurückzugeben, und ohne Annahmen über seine Entwicklungsumgebung oder Sprachversion zu treffen, die über das hinausgehen, was vom Code-Snippet abgeleitet werden könnte - das heißt, es ist sicher ES6 anzunehmen, aber nicht unbedingt ES7.
Jesse Amano

@ AhmadBamieh Alles klar, wird reichen. Ich gehe davon aus, dass das Problem darin besteht, dass ich falsch verstanden habe, wie returnmit der neuen (ish) Abschlusssyntax umgegangen wird. In diesem Fall lehne ich das stark ab, aber der Fehler liegt immer noch bei mir und ich entschuldige mich dafür.
Jesse Amano

2
@AhmadBamieh Ähm, ich kannte diesen Teil tatsächlich, weshalb ich behauptete, dass er token => { return token; } nichts tut, anstatt zu behaupten, er sei kontraproduktiv. Sie können für immer sagen google.login(username, password).then(token=>{return token;}).then(token=>{return token;})und so weiter, aber Sie werden nur eine Rückgabe erreichen Promise, die mit einem Token aufgelöst wird - so, als hätten Sie es einfach so gelassen google.login(username, password);. Ich bin mir nicht sicher, warum Sie das für "sehr falsch" halten.
Jesse Amano

1
@AhmadBamieh: Können Sie genauer sagen, was in diesem Text falsch ist? Ich sehe nichts, er erklärt nur, warum return tokenes nicht so funktioniert, wie es das OP wahrscheinlich erwartet hat.
Bergi

3
@ AhmadBamieh: Es gibt tatsächlich ein Missverständnis. Wir alle drei wissen genau, wie Versprechen funktionieren. Die Aussage ist promise.then(result => { return result; })genau gleichbedeutend mit promise, daher bewirkt der Methodenaufruf nichts und sollte gelöscht werden, um den Code zu vereinfachen und die Lesbarkeit zu verbessern - eine Aussage, die völlig richtig ist.
Bergi

0

Ihr Versprechen steht noch aus. Vervollständigen Sie es bis

userToken.then(function(result){
console.log(result)
})

nach Ihrem verbleibenden Code. Dieser Code erfüllt .then()lediglich Ihr Versprechen und erfasst das Endergebnis in der Ergebnisvariablen und druckt das Ergebnis in der Konsole. Beachten Sie, dass Sie das Ergebnis nicht in einer globalen Variablen speichern können. Hoffe, dass die Erklärung Ihnen helfen könnte.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.