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

Posted

技术标签:

【中文标题】为啥我的异步函数返回 Promise <pending> 而不是一个值?【英文标题】:Why is my asynchronous function returning Promise <pending> instead of a value?为什么我的异步函数返回 Promise <pending> 而不是一个值? 【发布时间】:2018-07-14 04:39:15 【问题描述】:

我的代码:

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

【问题讨论】:

@LoïcFaure-Lacroix,见这篇文章:medium.com/@bluepnume/… @LoïcFaure-Lacroix 看看getFirstUser 函数 那又如何呢?这是一个返回承诺的函数。 @LoïcFaure-Lacroix 所以你的意思是即使在那个例子中我们也需要使用 then 来访问在 getFirstUser 函数中返回的数据承诺? 在那个例子中是的,唯一的另一种方法是使用 ES7 语法“等待”,它似乎可以解决停止当前上下文的执行以等待承诺的结果。如果你阅读这篇文章,你会看到它。但是由于 ES7 可能几乎还没有在任何地方得到支持,是的。 “那么”差不多就是这样。 【参考方案1】:

只要其结果尚未解决,promise 将始终记录挂起。无论承诺状态如何(已解决或仍待处理),您都必须在承诺上调用 .then 以捕获结果:

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 只是向前的方向;您只能解决它们一次。 Promise 的解析值被传递给它的 .then.catch 方法。

详情

根据 Promises/A+ 规范:

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

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

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

如果.then 处理程序中的函数返回一个值,则Promise 将使用该值解析。如果处理程序返回另一个Promise,则原始Promise 将使用链接Promise 的解析值解析。下一个 .then 处理程序将始终包含在前面的 .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 函数返回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"
  );

【讨论】:

您的第一个不工作。 Uncaught SyntaxError: Unexpected token .。第二个需要返回Promise @zamil 您必须调用该函数,就像在第二个示例中一样。你不能 .then 在未调用的函数上。更新了答案 我正在为它添加书签,以便我可以永远保留它。我已经工作了很长时间,以找到真正清晰易读的如何实际构建 Promise 的规则。你对 Promises/A+ 规范的引用是一个完美的例子,说明为什么它是一个自学承诺的 PITA。这也是我唯一一次看到 setTimeout 在不混淆课程本身的情况下使用。非常好的参考,谢谢。 将结果赋值给另一个变量时不起作用。 它仍然返回 Promise 【参考方案2】:

我知道这个问题是 2 年前提出的,但我遇到了同样的问题,问题的答案是从 ES2017 开始,您可以简单地 await 函数返回值(截至目前,仅适用于 @ 987654323@ 函数),例如:

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


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

【讨论】:

你不需要.then(token =&gt; return token),这只是一个不必要的传递。只需返回 google 登录调用。 这个答案与问题无关。原贴的问题与 ES6 的 async/await 无关。在 ECMAScript 2017 中引入这种新的语法糖之前,Promise 就已经存在,并且他们在“幕后”使用了 Promises。见MDN on async/await。 对于 ES8 / Nodejs,如果您在异步函数之外使用 await,则会引发错误。也许这里更好的例子是使AuthUser函数async,然后以return await google.login(...);结束 这对我有用,而且是一个更简单的解决方案。不要忘记使用异步。 它给了我这个错误``` let answer = await getAnswer(url); ^^^^^ SyntaxError: await 仅在异步函数和模块的顶层主体中有效```【参考方案3】:

then 方法返回一个待处理的 Promise,它可以通过在调用 then 时注册的结果处理程序的返回值异步解决,或者通过在被调用的处理程序内抛出错误来拒绝。

因此调用AuthUser 不会突然同步登录用户,而是返回一个promise,在登录成功(或失败)后将调用其注册的处理程序。我建议通过登录承诺的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);

【讨论】:

【参考方案4】:

查看Promises.上的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 =&gt; return token; 什么都不做;相反,您对then() 的输入需要是一个以某种方式实际处理令牌的函数。

【讨论】:

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

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

[ 
  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

【讨论】:

【参考方案6】:

您的承诺正在等待中,请通过

完成
userToken.then(function(result)
console.log(result)
)

在您剩余的代码之后。 这段代码所做的就是.then() 完成您的承诺并在 result 变量中捕获最终结果并在控制台中打印结果。 请记住,您不能将结果存储在全局变量中。 希望解释对您有所帮助。

【讨论】:

控制台日志有必要吗?如果插入调试器呢?这似乎改变了行为。【参考方案7】:

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

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

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

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

而不是:

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

【讨论】:

【参考方案8】:

试试这个

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 />

【讨论】:

以上是关于为啥我的异步函数返回 Promise <pending> 而不是一个值?的主要内容,如果未能解决你的问题,请参考以下文章

为啥我的函数返回 Promise <pending> [重复]

useState 与异步函数返回 Promise <pending>

异步支持返回不可链接的 Promise<void> 的函数

异步函数返回 Promise <Pending>

异步函数的返回类型必须是全局 Promise<T> 类型

为啥 Body.json() 返回一个 Promise? [复制]