Pourquoi ma fonction asynchrone renvoie-t-elle Promise {<pending>} au lieu d'une valeur?

129

Mon code:

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

Et quand j'essaye d'exécuter quelque chose comme ça:

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

Je suis en train:

Promise { <pending> }

Mais pourquoi?

Mon objectif principal est d'obtenir un jeton à partir google.login(data.username, data.password)duquel retourne une promesse, dans une variable. Et alors seulement, effectuez certaines actions.

Src
la source
1
@ LoïcFaure-Lacroix, voir cet article: medium.com/@bluepnume
Src
@ LoïcFaure-Lacroix regarde la getFirstUserfonction
Src
Alors qu'en est-il? C'est une fonction qui renvoie une promesse.
Loïc Faure-Lacroix
1
@ LoïcFaure-Lacroix donc vous voulez dire que même dans cet exemple, nous devons utiliser alors pour accéder à la promesse de données retournant dans la fonction getFirstUser?
Src
Dans cet exemple oui, le seul autre moyen est d'utiliser la syntaxe ES7 "await" qui semble résoudre l'arrêt de l'exécution du contexte courant pour attendre le résultat de la promesse. Si vous lisez l'article, vous le verrez. Mais puisque ES7 n'est probablement presque supporté nulle part encore, oui. Le «alors» est à peu près tout.
Loïc Faure-Lacroix

Réponses:

176

La promesse sera toujours enregistrée en attente tant que ses résultats ne sont pas encore résolus. Vous devez faire appel .thenà la promesse pour capturer les résultats quel que soit l'état de la promesse (résolu ou toujours en attente):

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

Pourquoi donc?

Les promesses sont uniquement orientées vers l'avant; Vous ne pouvez les résoudre qu'une seule fois. La valeur résolue de a Promiseest transmise à ses méthodes .thenou .catch.

Détails

Selon les promesses / spécifications A +:

La procédure de résolution de promesse est une opération abstraite prenant comme entrée une promesse et une valeur, que nous notons [[Resolve]] (promise, x). Si x est un alorsable, il tente de faire adopter par promesse l'état de x, sous l'hypothèse que x se comporte au moins un peu comme une promesse. Sinon, il tient la promesse avec la valeur x.

Ce traitement des thenables permet aux implémentations de promesse d'interopérer, tant qu'elles exposent une méthode alors conforme à Promises / A +. Il permet également aux implémentations Promises / A + «d'assimiler» les implémentations non conformes avec des méthodes alors raisonnables.

Cette spécification est un peu difficile à analyser, alors décomposons-la. La règle est:

Si la fonction dans le .thengestionnaire renvoie une valeur, alors la Promiserésolution avec cette valeur. Si le gestionnaire en renvoie un autre Promise, l'original est Promiserésolu avec la valeur résolue de la chaîne Promise. Le .thengestionnaire suivant contiendra toujours la valeur résolue de la promesse chaînée retournée dans le précédent .then.

La façon dont cela fonctionne réellement est décrite ci-dessous plus en détail:

1. Le retour de la .thenfonction sera la valeur résolue de la promesse.

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. Si la .thenfonction renvoie a Promise, la valeur résolue de cette promesse chaînée est transmise au suivant .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"
  });
Bamieh
la source
Votre premier ne fonctionne pas. Uncaught SyntaxError: Unexpected token .. Le second a besoin d'un retour pourPromise
zamil
@zamil vous devez appeler la fonction, comme dans le deuxième exemple. vous ne pouvez pas .thensur une fonction non invoquée. a mis à jour la réponse
Bamieh
1
Je mets ceci en favori pour pouvoir le garder pour toujours. Je travaille TRES longtemps pour trouver des règles vraiment claires et lisibles sur la façon de construire des promesses. Votre blockquote de Promises / A + spec est un exemple parfait de la raison pour laquelle il a été un PITA d'auto-enseigner les promesses. C'est aussi la SEULE fois que j'ai vu setTimeout utilisé là où il ne confondait pas la leçon elle-même. Et excellente référence, merci.
monsto
21

Je sais que cette question a été posée il y a 2 ans, mais je rencontre le même problème et la réponse au problème est depuis ES6, que vous pouvez simplement awaitrenvoyer la valeur des fonctions, comme:

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
Marius Seack
la source
3
Vous n'avez pas besoin du .then(token => return token), c'est juste un passthrough inutile. Renvoyez simplement l'appel de connexion google.
Soviut
Cette réponse n'a aucun rapport avec la question. Le problème de l'affiche originale n'a rien à voir avec async / await d'ES6. Les promesses existaient avant que ce nouveau sucre syntaxique ne soit introduit dans ECMAScript 2017 et ils utilisaient des promesses «sous le capot». Voir MDN sur async / await .
try-catch-finally
Pour ES8 / Nodejs, des erreurs sont générées si vous utilisez en awaitdehors d'une fonction asynchrone. Peut-être que le meilleur exemple ici serait de faire la AuthUserfonction async, qui se termine ensuite parreturn await google.login(...);
Jon L.
4

La thenméthode retourne une promesse en attente qui peut être résolue de manière asynchrone par la valeur de retour d'un gestionnaire de résultats enregistré dans l'appel à then, ou rejetée en lançant une erreur dans le gestionnaire appelé.

Ainsi, l'appel AuthUserne connectera pas soudainement l'utilisateur de manière synchrone, mais retournera une promesse dont les gestionnaires alors enregistrés seront appelés après la réussite (ou l'échec) de la connexion. Je suggérerais de déclencher tout traitement de connexion par une thenclause de la promesse de connexion. EG en utilisant des fonctions nommées pour mettre en évidence la séquence de flux:

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);
}
traktor53
la source
1

Voir la section MDN sur les promesses. En particulier, regardez le type de retour de then ().

Pour se connecter, l'agent utilisateur doit soumettre une requête au serveur et attendre de recevoir une réponse. Étant donné que faire en sorte que votre application arrête totalement l'exécution pendant un aller-retour de demande entraîne généralement une mauvaise expérience utilisateur, pratiquement toutes les fonctions JS qui vous connectent (ou exécutent toute autre forme d'interaction avec le serveur) utiliseront une promesse, ou quelque chose de très similaire. , pour fournir des résultats de manière asynchrone.

Maintenant, notez également que les returninstructions sont toujours évaluées dans le contexte de la fonction dans laquelle elles apparaissent. Ainsi, lorsque vous avez écrit:

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

l'instruction return token;signifiait que la fonction anonyme transmise then()devait renvoyer le jeton, pas que la AuthUserfonction le devrait. Ce qui AuthUserrevient est le résultat de l'appel google.login(username, password).then(callback);, qui se trouve être une promesse.

En fin de compte, votre rappel token => { return token; }ne fait rien; au lieu de cela, votre entrée then()doit être une fonction qui gère réellement le jeton d'une manière ou d'une autre.

Jesse Amano
la source
@Src J'ai écrit ma réponse avant que le demandeur ne précise qu'il cherchait un moyen de renvoyer une valeur de manière synchrone , et sans faire d'hypothèses sur leur environnement de développement ou leur version de langue au-delà de ce qui pourrait être déduit par l'extrait de code - c'est-à-dire que c'est sûr pour assumer ES6, mais pas nécessairement ES7.
Jesse Amano
@AhmadBamieh Très bien, fera l'affaire. Je suppose que le problème est que j'ai mal compris comment returnest traitée la nouvelle syntaxe de fermeture (ish), auquel cas - eh bien, je désapprouve fortement cela, mais l'erreur est toujours la mienne et je m'en excuse.
Jesse Amano
2
@AhmadBamieh Er, je connaissais en fait cette partie, c'est pourquoi j'ai affirmé que token => { return token; } cela ne faisait rien au lieu de prétendre que c'était contre-productif. Vous pouvez dire google.login(username, password).then(token=>{return token;}).then(token=>{return token;})et ainsi de suite pour toujours, mais vous n'obtiendrez le retour d'un Promisequi se résout avec un jeton - comme si vous le laissiez comme google.login(username, password);. Je ne sais pas pourquoi vous pensez que c'est "très faux".
Jesse Amano
1
@AhmadBamieh: pouvez-vous être plus précis sur ce qui ne va pas dans ce texte? Je ne vois rien, il explique simplement pourquoi return tokenne fonctionne pas comme prévu par l'OP.
Bergi
3
@AhmadBamieh: il y a effectivement un malentendu. Nous savons tous les trois comment fonctionnent les promesses, la déclaration est promise.then(result => { return result; })exactement équivalente à promise, donc l'appel de méthode ne fait rien et doit être abandonné pour simplifier le code et améliorer la lisibilité - une déclaration qui est tout à fait vraie.
Bergi
1

Votre promesse est en attente, complétez-la en

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

après votre code restant. Tout ce que fait ce code, c'est qu'il .then()complète votre promesse et capture le résultat final dans la variable de résultat et le résultat d'impression dans la console. Gardez à l'esprit que vous ne pouvez pas stocker le résultat dans une variable globale. J'espère que cette explication pourrait vous aider.

Naveen Nirban Yadav
la source