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


127

我的代码:

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)中返回承诺)转换为变量。然后才执行一些操作。


1
@LoïcFaure-Lacroix,请参阅此文章:medium.com/@bluepnume/…–
Src,

@LoïcFaure-Lacroix看getFirstUser功能
Src

那呢?这是一个返回承诺的函数。
卢瓦克福雷-拉克鲁瓦

1
@LoïcFaure-Lacroix,所以您的意思是即使在该示例中,我们也需要使用then来访问getFirstUser函数中返回的数据承诺?
Src

在该示例中,是,唯一的其他方法是使用似乎可以解决的ES7语法“ await”,即停止执行当前上下文以等待promise的结果。如果您阅读该文章,将会看到它。但是由于ES7可能几乎没有支持,所以可以。“那么”就差不多了。
卢瓦克福雷-拉克鲁瓦

Answers:


175

只要其结果尚未解决,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"
})

这是为什么?

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

细节

根据Promises / A +规范:

承诺解决程序是一个抽象操作,将承诺和值作为输入,我们将其表示为[[Resolve]](promise,x)。如果x是可能的,则在x的行为至少类似于承诺的假设下,尝试使承诺采用x的状态。否则,它会以值x履行承诺。

只要有实现Promises / A +兼容的then方法,这种对ableable的处理就可以使promise实现互操作。它还允许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函数返回a 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)'17

@zamil,您必须像第二个示例中一样调用该函数。您不能.then使用未调用的功能。更新了答案
巴米耶

1
我将其添加为书签,以便永远保存我已经花了很长时间来找到关于如何真正建立承诺的真正清晰可读的规则。您的Promises / A +规范的大宗报价就是一个很好的例子,说明为什么它已成为自学承诺的PITA。这也是我看到setTimeout不会混淆课程本身的唯一时间。并提供出色的参考,谢谢。
monsto

21

我知道这个问题是在2年前提出的,但是我遇到了同样的问题,而该问题的答案是从ES6开始,您可以简单地await将函数返回值,例如:

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
您不需要.then(token => return token),这只是不必要的传递。只需返回google登录电话即可。
苏联

这个答案与问题无关。原始海报的问题与ES6的异步/等待无关。在ECMAScript 2017中将这种新的语法糖引入之前,就存在承诺,并且他们在“幕后”使用了承诺。请参阅async / await上的MDN
试试,最终

对于ES8 / Nodejs,如果await在异步函数之外使用,则会引发错误。也许这里更好的例子是制作AuthUser函数async,然后以return await google.login(...);
Jon L.

4

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

因此,调用AuthUser不会突然使用户同步登录,而是返回一个Promise,登录成功(或失败)后将调用该Prom然后注册的处理程序。我建议通过then登录承诺的子句触发所有登录处理。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);
}

1

请参阅“ 承诺”中的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);,这恰好是一个承诺。

最终,您的回调token => { return token; }不执行任何操作;取而代之的是,您对的输入必须then()是实际上以某种方式处理令牌的函数。


@Src我在提问者澄清之前写了我的答案,他们正在寻找一种同步返回值的方法,并且无需对代码环境可以推断出的开发环境或语言版本进行假设,也就是说,这是安全的假设采用ES6,但不一定采用ES7。
Jesse Amano

@AhmadBamieh好的,会的。我以为问题是我误解return了新的(ish)闭合语法的处理方式,在这种情况下-恩,我对此表示强烈反对,但错误仍然是我的,对此我深表歉意。
Jesse Amano

2
@AhmadBamieh,我实际上确实知道那部分,这就是为什么我断言token => { return token; } 什么都不做,而不是声称它适得其反。您可以一直说google.login(username, password).then(token=>{return token;}).then(token=>{return token;})下去,但只能实现返回Promise用令牌解析的a -就像您只是将其保留为一样google.login(username, password);。我不确定您为什么会认为这是“非常错误”。
Jesse Amano

1
@AhmadBamieh:您能更详细地说明这段文字中的错误吗?我什么也没看到,他只是解释了为什么return tokenOP可能无法正常工作。
Bergi '16

3
@AhmadBamieh:确实存在误解。我们三个都很了解promise是如何工作的,该语句promise.then(result => { return result; })完全等于promise,因此该方法调用不执行任何操作,应将其删除以简化代码并增强可读性-完全正确的语句。
Bergi

0

您的承诺尚未完成,请按以下步骤完成

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

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

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.