为什么我的异步函数返回Promise {<pending>}而不是一个值?

我的代码:

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

当我试图运行这样的东西:

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

我得到:

Promise { <pending> }

但是为什么呢?

我的主要目标是从google.login(data.username, data.password)中获取令牌,它返回一个承诺,到一个变量中。然后再执行一些动作。

535675 次浏览

请参阅承诺。的MDN部分。特别地,请查看然后()。的返回类型

要登录,用户代理必须向服务器提交请求并等待接收响应。由于在请求往返期间让应用程序完全停止执行通常会导致糟糕的用户体验,实际上每个让您登录(或执行任何其他形式的服务器交互)的JS函数都将使用Promise或非常类似的东西来异步交付结果。

现在,还要注意return语句总是在它们出现的函数的上下文中求值。所以当你写:

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

语句return token;意味着传递给then()的匿名函数应该返回令牌,而不是AuthUser函数应该返回令牌。AuthUser返回的是调用google.login(username, password).then(callback);的结果,而google.login(username, password).then(callback);恰好是一个Promise。

最终你的回调token => { return token; }什么都不做;相反,你对then()的输入需要是一个以某种方式实际处理标记的函数。

then方法返回一个未决承诺,该承诺可以通过在then调用中注册的结果处理程序的返回值异步解析,或者通过在被调用的处理程序中抛出错误来拒绝。

因此调用AuthUser将不会突然同步地让用户登录,而是返回一个承诺,在登录成功(或失败)后将调用其当时注册的处理程序。我建议通过登录承诺的then子句触发所有登录处理。例如,使用命名函数来突出显示流的顺序:

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

只要promise的结果还没有被解决,它就总是记录挂起的日志。你必须在promise上调用.then来捕获结果,而不管promise状态如何(已解决或仍待处理):

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

为什么呢?

承诺只是前进的方向;你只能解决一次。Promise的解析值被传递给它的.then.catch方法。

细节

根据承诺/A+规范:

承诺解析过程是一个抽象操作,取 输入一个承诺和一个值,我们将其表示为[[Resolve]](承诺, 如果x是一个使能,它试图使承诺采用状态 X,在假设X至少有点像a的情况下 的承诺。否则,它以x值履行承诺。

这种对thenabling的处理允许promise实现 互操作,只要他们暴露一个承诺/ a +兼容 方法。它还允许Promises/A+实现“同化”

这个规范有点难以解析,所以让我们详细分析一下。规则是:

如果.then处理程序中的函数返回一个值,则Promise用该值进行解析。如果处理程序返回另一个Promise,则原始的Promise使用被链接的Promise的已解析值进行解析。下一个.then处理程序将始终包含在前一个.then中返回的链式承诺的解析值。

它的实际工作方式如下所述:

1. .then函数的返回值将是承诺的解析值。

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. 如果.then函数返回Promise,则该链式承诺的解析值将传递给下面的.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"
});

我知道这个问题在2年前被问过,但我遇到了同样的问题,这个问题的答案是自ES2017以来,你可以简单地await函数返回值(截至目前,只适用于async函数),比如:

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


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

你的承诺正在等待,通过完成它

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

在你剩余的代码之后。 所有这些代码所做的是.then()完成你的承诺&捕获结果变量&中的最终结果;在控制台打印结果。 请记住,不能将结果存储在全局变量中。

.

.

我之前也遇到过同样的问题,但我在前端的情况有点不同。我还是会分享我的场景,也许有人会觉得有用。

我有一个api调用/api/user/register在前端的电子邮件,密码和用户名作为请求体。在提交表单(注册表单)时,调用一个处理程序函数,该函数将启动对/api/user/register的取回调用。我在这个处理函数的开始行中使用了event.preventDefault(),所有其他行,比如形成请求体以及fetch调用都写在event.preventDefault()之后。返回pending promise

但是当我把请求主体构造代码放在event.preventDefault()之上时,它返回了真正的承诺。是这样的:

event.preventDefault();
const data = {
'email': email,
'password': password
}
fetch(...)
...

而不是:

     const data = {
'email': email,
'password': password
}
event.preventDefault();
fetch(...)
...

如果这种情况发生在一个多值的数组中。

[
Promise { <pending> },
Promise { <pending> },
Promise { <pending> },
Promise { <pending> },
Promise { <pending> }
]

你可以使用Promise.all()来解决所有的承诺。

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/all

试试这个

var number1 = document.getElementById("number1");
var number2 = document.getElementById("number2");
startAsync.addEventListener("click", function() {
if (number1.value > 0 && number2.value > 0) {
asyncTest(parseInt(number1.value), parseInt(number2.value)).then(function(result) {
document.getElementById("promiseResolved").textContent = "promiseResolved: " + result
});
} else {
asyncTest(1, 2).then(function(result) {
document.getElementById("promiseResolved").textContent = "promiseResolved: " + result
});
}


});


async function asyncTest(a, b) {
return await (a + b);
};
  <button id="startAsync">start Async function</button><br />
<input type="number" id="number1" /><br />
<input type="number" id="number2" /><br />
<span id="promiseResolved"></span><br />

我的情况(JS),我忘记添加等待