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

IT技术 javascript node.js promise
2021-01-26 17:44:45

我的代码:

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)返回Promise的令牌获取到变量中。然后才执行一些操作。

6个回答

只要其结果尚未解决,promise 将始终记录挂起。.then无论Promise状态如何(已解决或仍在等待),您都必须调用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"
})

这是为什么?

Promises 只是向前的;您只能解决一次。a 的解析值Promise传递给它的.then.catch方法。

细节

根据 Promises/A+ 规范:

Promise解析过程是一个抽象操作,将Promise和值作为输入,我们将其表示为 [[Resolve]](promise, x)。如果 x 是一个 thenable,它会尝试让 promise 采用 x 的状态,假设 x 的行为至少有点像 promise。否则,它以值 x 履行Promise。

对 thenables 的这种处理允许 promise 实现互操作,只要它们公开一个符合 Promises/A+ 的 then 方法。它还允许 Promises/A+ 实现用合理的 then 方法“同化”不一致的实现。

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

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

下面更详细地描述了它的实际工作方式:

1..then函数的返回值是promise 的解析值。

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函数返回 a Promise,则该链式 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"
  });
我正在为它添加书签,以便我可以永远保留它我一直在努力寻找真正清晰易读的关于如何构建Promise的规则。你对 Promises/A+ 规范的引用是一个完美的例子,说明为什么它是自学 Promise 的 PITA。这也是我唯一一次看到 setTimeout 在没有混淆课程本身的情况下使用。和很好的参考,谢谢。
2021-03-17 17:44:45
它仍然返回 Promise { <Pending> }
2021-03-23 17:44:45
将结果分配给另一个变量时它不起作用。
2021-03-31 17:44:45
@zamil 您必须调用该函数,就像在第二个示例中一样。你不能.then使用未调用的函数。更新了答案
2021-04-04 17:44:45
你的第一个不工作。Uncaught SyntaxError: Unexpected token .. 第二个需要回报Promise
2021-04-09 17:44:45

我知道这个问题是 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
这对我有用,是一个更简单的解决方案imo。不要忘记使用异步。
2021-03-10 17:44:45
它给了我这个错误``` let answer = await getAnswer(url); ^^^^^ 语法错误:await 仅在异步函数和module的顶级主体中有效```
2021-03-14 17:44:45
你不需要.then(token => return token),那只是一个不必要的传递。只需返回谷歌登录调用。
2021-03-16 17:44:45
这个答案与问题无关。原贴的问题与ES6的async/await无关。在 ECMAScript 2017 中引入这种新的语法糖之前,Promise 就已经存在,他们在“幕后”使用了 Promise。请参阅async/await 上的 MDN
2021-03-26 17:44:45
对于 ES8 / Nodejs,如果await在异步函数之外使用,则会引发错误也许这里更好的例子是创建AuthUser函数async,然后以return await google.login(...);
2021-03-31 17:44:45

then方法返回一个挂起的Promise,它可以通过调用中注册的结果处理程序的返回值异步解析then,或者通过在调用的处理程序中抛出错误拒绝。

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

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的 MDN 部分特别是看一下then()的返回类型

要登录,用户代理必须向服务器提交请求并等待接收响应。由于在请求往返期间让您的应用程序完全停止执行通常会导致糟糕的用户体验,因此实际上每个登录您(或执行任何其他形式的服务器交互)的 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);,这恰好是一个Promise。

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

@AhmadBamieh:您能否更具体地说明这段文字中的错误?我什么也没看到,他只是解释了为什么return token不像 OP 预期的那样工作。
2021-03-11 17:44:45
@AhmadBamieh Er,我实际上确实知道那部分,这就是为什么我断言这token => { return token; } 没有任何作用,而不是声称它适得其反。你可以一直说google.login(username, password).then(token=>{return token;}).then(token=>{return token;})等等,但你只能实现返回一个Promise用令牌解析的 a - 就像你刚刚把它留在google.login(username, password);. 我不知道你为什么觉得这是“非常错误的”。
2021-03-15 17:44:45
@Src 我在提问者澄清他们正在寻找一种同步返回值的方法之前写了我的答案,并且没有对他们的开发环境或语言版本做出超出代码片段推断的假设——也就是说,它是安全的假设 ES6,但不一定是 ES7。
2021-03-22 17:44:45
@AhmadBamieh 好吧,会的。我假设问题是我误解return了 new(ish) 闭包语法是如何处理的,在这种情况下 - 好吧,我强烈反对这一点,但错误仍然是我的,我为此道歉。
2021-04-06 17:44:45
@AhmadBamieh:确实存在误解。我们三个都很清楚 promises 是如何工作的,这句话promise.then(result => { return result; })完全等同于promise,因此方法调用什么都不做,应该删除以简化代码并提高可读性——这句话是完全正确的。
2021-04-06 17:44:45

如果这种情况发生在像数组这样的多个值上。

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

您可以使用Promise.all()这将解决所有Promise。

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