Perché la mia funzione asincrona restituisce Promise {<pending>} invece di un valore?


128

Il mio codice:

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

E quando provo a eseguire qualcosa del genere:

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

Sto ottenendo:

Promise { <pending> }

Ma perché?

Il mio obiettivo principale è ottenere un token da google.login(data.username, data.password)cui restituisce una promessa, in una variabile. E solo allora preforma alcune azioni.


1
@ LoïcFaure-Lacroix, vedi questo articolo: medium.com/@bluepnume/…
Src

@ LoïcFaure-Lacroix guarda la getFirstUserfunzione
Src

E allora? È una funzione che restituisce una promessa.
Loïc Faure-Lacroix

1
@ LoïcFaure-Lacroix quindi intendi anche in quell'esempio che dobbiamo usare per accedere alla promessa di dati restituita nella funzione getFirstUser?
Src

In quell'esempio sì, l'unico altro modo è usare la sintassi ES7 "await" che sembra risolvere fermare l'esecuzione del contesto corrente per attendere il risultato della promessa. Se leggi l'articolo lo vedrai. Ma poiché ES7 è probabilmente quasi ancora supportato da nessuna parte, sì. Il "allora" è praticamente tutto.
Loïc Faure-Lacroix

Risposte:


175

La promessa sarà sempre registrata in sospeso fino a quando i suoi risultati non saranno ancora risolti. È necessario chiamare .thenla promessa per acquisire i risultati indipendentemente dallo stato della promessa (risolta o ancora in sospeso):

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

Perché?

Le promesse sono solo una direzione in avanti; Puoi risolverli solo una volta. Il valore risolto di a Promiseviene passato ai suoi metodi .theno .catch.

Dettagli

Secondo le specifiche Promises / A +:

La procedura di risoluzione della promessa è un'operazione astratta che prende come input una promessa e un valore, che indichiamo come [[Risolvi]] (promessa, x). Se x è una variabile, tenta di fare in modo che la promessa adotti lo stato di x, assumendo che x si comporti almeno in qualche modo come una promessa. Altrimenti, soddisfa la promessa con il valore x.

Questo trattamento degli oggetti di valore consente alle implementazioni delle promesse di interagire, purché espongano un metodo conforme alle promesse / A +. Consente inoltre alle implementazioni Promises / A + di "assimilare" implementazioni non conformi con metodi ragionevoli.

Questa specifica è un po 'difficile da analizzare, quindi analizziamola. La regola è:

Se la funzione nel .thengestore restituisce un valore, allora si Promiserisolve con quel valore. Se il gestore ne restituisce un altro Promise, l'originale si Promiserisolve con il valore risolto del concatenato Promise. Il .thengestore successivo conterrà sempre il valore risolto della promessa concatenata restituita nel precedente .then.

Il modo in cui funziona effettivamente è descritto di seguito in modo più dettagliato:

1. Il ritorno della .thenfunzione sarà il valore risolto della promessa.

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. Se la .thenfunzione restituisce a Promise, il valore risolto di quella promessa concatenata viene passato al seguente .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"
  });

Il tuo primo non funziona. Uncaught SyntaxError: Unexpected token .. Il secondo ha bisogno di un ritorno perPromise
zamil

@zamil devi invocare la funzione, come nel secondo esempio. non puoi .thensu una funzione non invocata. ha aggiornato la risposta
Bamieh

1
Lo aggiungo ai segnalibri in modo da poterlo conservare per sempre. Ho lavorato MOLTO a lungo per trovare regole veramente chiare e leggibili su come costruire effettivamente le promesse. La tua blockquote di Promises / A + spec è un perfetto esempio del motivo per cui è stata una PITA insegnare da sé le promesse. È anche l'UNICA volta che ho visto setTimeout utilizzato in cui non ha confuso la lezione stessa. E ottimo riferimento, grazie.
monsto

21

So che questa domanda è stata posta 2 anni fa, ma mi imbatto nello stesso problema e la risposta al problema è da ES6, che puoi semplicemente awaitle funzioni restituire un valore, come:

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
Non hai bisogno del .then(token => return token), è solo un passaggio non necessario. Restituisci semplicemente la chiamata di accesso a Google.
Soviut

Questa risposta non è correlata alla domanda. Il problema del poster originale non ha nulla a che fare con l'asincronia / attesa di ES6. Le promesse esistevano prima che questo nuovo zucchero sintattico fosse introdotto in ECMAScript 2017 e usavano Promesse "sotto il cofano". Vedi MDN su async / await .
prova a catturare finalmente il

Per ES8 / Nodejs, vengono generati errori se si utilizza awaital di fuori di una funzione asincrona. Forse l'esempio migliore qui sarebbe creare la AuthUserfunzione async, che poi termina conreturn await google.login(...);
Jon L.

4

Il thenmetodo restituisce una promessa in sospeso che può essere risolta in modo asincrono dal valore di ritorno di un gestore dei risultati registrato nella chiamata athen , o rifiutata lanciando un errore all'interno del gestore chiamato.

Quindi la chiamata AuthUsernon registrerà improvvisamente l'utente in modo sincrono, ma restituirà una promessa i cui gestori registrati in quel momento verranno chiamati dopo che l'accesso riesce (o fallisce). Suggerirei di attivare tutta l'elaborazione dell'accesso tramite una thenclausola della promessa di accesso. Ad esempio utilizzando funzioni denominate per evidenziare la sequenza del flusso:

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

Vedere la sezione MDN sulle promesse. In particolare, guarda il tipo restituito di then ().

Per accedere, lo user-agent deve inviare una richiesta al server e attendere di ricevere una risposta. Dal momento che interrompere completamente l'esecuzione della tua applicazione durante un round-trip della richiesta di solito crea un'esperienza utente negativa, praticamente ogni funzione JS che ti accede (o esegue qualsiasi altra forma di interazione con il server) utilizzerà una promessa, o qualcosa di molto simile , per fornire risultati in modo asincrono.

Ora, nota anche che le returnistruzioni sono sempre valutate nel contesto della funzione in cui compaiono. Quindi, quando hai scritto:

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

l'istruzione return token;significava che la funzione anonima a cui si stava passando then()dovrebbe restituire il token, non che la AuthUserfunzione dovrebbe. Ciò che AuthUserritorna è il risultato della chiamata google.login(username, password).then(callback);, che sembra essere una promessa.

Alla fine la tua richiamata token => { return token; }non fa nulla; invece, il tuo input then()deve essere una funzione che effettivamente gestisce il token in qualche modo.


@Src Ho scritto la mia risposta prima che il richiedente chiarisse che stavano cercando un modo per restituire un valore in modo sincrono e senza fare supposizioni sul loro ambiente di sviluppo o versione del linguaggio oltre a ciò che potrebbe essere dedotto dallo snippet di codice - cioè, è sicuro assumere ES6, ma non necessariamente ES7.
Jesse Amano

@AhmadBamieh Va bene, va bene. Presumo che il problema sia che ho frainteso come returnviene trattata con la nuova sintassi di chiusura (ish), nel qual caso - beh, lo disapprovo fortemente, ma l'errore è ancora mio e me ne scuso.
Jesse Amano

2
@AhmadBamieh Er, in realtà conoscevo quella parte, motivo per cui ho affermato che token => { return token; } non fa nulla invece di affermare che era controproducente. Puoi dire google.login(username, password).then(token=>{return token;}).then(token=>{return token;})e così via per sempre, ma otterrai solo la restituzione di un Promiseche si risolve con un gettone, come se lo avessi lasciato come google.login(username, password);. Non sono sicuro del motivo per cui ritieni che sia "molto sbagliato".
Jesse Amano

1
@AhmadBamieh: puoi essere più specifico su cosa c'è di sbagliato in questo pezzo di testo? Non vedo nulla, spiega solo perché return tokennon funziona come probabilmente previsto dall'OP.
Bergi

3
@AhmadBamieh: c'è davvero un malinteso. Sappiamo tutti e tre bene come funzionano le promesse, l'affermazione è promise.then(result => { return result; })esattamente equivalente a promise, quindi la chiamata al metodo non fa nulla e dovrebbe essere eliminata per semplificare il codice e migliorare la leggibilità - un'affermazione che è completamente vera.
Bergi

1

La tua promessa è in sospeso, completala entro

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

dopo il codice rimanente. Tutto ciò che fa questo codice è che .then()completa la tua promessa e cattura il risultato finale nella variabile del risultato e stampa il risultato nella console. Tieni presente che non puoi memorizzare il risultato nella variabile globale. Spero che questa spiegazione possa aiutarti.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.