¿Por qué mi función asincrónica devuelve Promise { <pending> } en lugar de un valor?
Mi código:
let AuthUser = data => {
return google.login(data.username, data.password).then(token => { return token } )
}
Y cuando intento ejecutar algo como esto:
let userToken = AuthUser(data)
console.log(userToken)
Me estoy poniendo:
Promise { <pending> }
¿Pero por qué?
Mi objetivo principal es convertir el token google.login(data.username, data.password)
que devuelve una promesa en una variable. Y sólo entonces realizar algunas acciones.
La promesa siempre quedará pendiente mientras sus resultados no se resuelvan aún. Debe invocar .then
la promesa para capturar los resultados independientemente del estado de la promesa (resuelta o aún pendiente):
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"
})
¿Porqué es eso?
Las promesas son sólo dirección hacia adelante; Sólo puedes resolverlos una vez. El valor resuelto de a Promise
se pasa a sus métodos .then
o .catch
.
Detalles
Según la especificación Promises/A+:
El procedimiento de resolución de promesa es una operación abstracta que toma como entrada una promesa y un valor, que denotamos como [[Resolve]](promesa, x). Si x es un entoncesable, intenta hacer que la promesa adopte el estado de x, bajo el supuesto de que x se comporta al menos de alguna manera como una promesa. En caso contrario, cumple la promesa con el valor x.
Este tratamiento de thenables permite que las implementaciones de promesas interoperen, siempre que expongan un método then compatible con Promises/A+. También permite que las implementaciones de Promises/A+ "asimilen" implementaciones no conformes con métodos entonces razonables.
Esta especificación es un poco difícil de analizar, así que analicémosla. La regla es:
Si la función en el .then
controlador devuelve un valor, entonces se Promise
resuelve con ese valor. Si el controlador devuelve otro Promise
, entonces el original Promise
se resuelve con el valor resuelto del encadenado Promise
. El siguiente .then
controlador siempre contendrá el valor resuelto de la promesa encadenada devuelta en el anterior .then
.
La forma en que realmente funciona se describe a continuación con más detalle:
1. El retorno de la .then
función será el valor resuelto de la promesa.
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 .then
función devuelve a Promise
, entonces el valor resuelto de esa promesa encadenada se pasa al siguiente .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"
});
Me encontré con el mismo problema y la respuesta al problema es que, desde ES2017, las await
funciones pueden simplemente devolver el valor (a partir de ahora, solo funciona en async
funciones), como:
let AuthUser = function(data) {
return google.login(data.username, data.password)
}
let userToken = await AuthUser(data)
console.log(userToken) // your data
Si esa situación ocurre para múltiples valores como una matriz.
[
Promise { <pending> },
Promise { <pending> },
Promise { <pending> },
Promise { <pending> },
Promise { <pending> }
]
Puede utilizar Promise.all()
esto para resolver todas las promesas.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/all