【问题标题】:Can you call an asynchronous function/method directly with a .then?你可以直接用 .then 调用异步函数/方法吗?
【发布时间】:2019-09-30 20:56:55
【问题描述】:

我在这里有一些代码,我正在使用 firebase auth 将用户登录到 firebase。我在我的 app.js 文件中实例化一个新用户,在该对象上调用 signIn 方法,然后在我的用户类的 signIn 方法中,我是控制台登录来自 firebase auth 的返回凭证。我还将凭证返回给调用它的对象并再次在控制台记录凭证。当我使用 async / await 时,代码会按照我的预期运行:它首先在 signIn 方法中记录凭据,然后在我调用 signIn 之后再次在 app.js 中记录。

但是,当我尝试使用 .then 方法执行此操作时,我的 app.js 文件中的 console.log 在控制台登录 User 类的 signIn 方法之前显示未定义。但是,User 类的 signIn 方法中的控制台日志会返回正确的凭据。

我的问题是:为什么我的 app.js 文件中的控制台日志没有在控制台日志记录之前等待获取凭据?或者至少控制台记录一个承诺?

   class User {
        constructor(email, password){
            this.email = email;
            this.password = password;
            this.cred;
        }

        async signUp(){
            const cred = await auth.createUserWithEmailAndPassword(this.email, this.password);
            return cred;
        }

        async signIn() {

            auth.signInWithEmailAndPassword(this.email,this.password).then(cred=>{
                console.log(cred);
                return cred;
            });

            //this async/await code below works as expected 
            // const cred = await auth.signInWithEmailAndPassword(this.email,this.password);
            // console.log(cred);
            // return cred;
        }

        signOut(){

        }
    }

    export default User


   // beginning of my app.js file

   loginForm && loginForm.addEventListener('submit', async e => {
        e.preventDefault();

        const user = new User(loginForm.email.value,loginForm.password.value);

        //const cred = await user.signIn(); // this async/await code works fine
        //console.log(cred); 

            user.signIn().then(cred => { 
              console.log(cred); // this console.log fires before the console.log in signIn() and returns undefined
              loginForm.reset();
            })
        });

【问题讨论】:

  • 你的signIn()需要返回里面的Promise。目前它什么也不返回,这就是为什么 user.signIn().then() 根本不应该工作。
  • 因为 signIn 没有返回承诺,所以它不是链的一部分。

标签: javascript firebase asynchronous promise


【解决方案1】:

首先,您应该注意到async/await 的功能本质上只是表达基于 Promise 的工作流的另一种方式。当您将函数标记为异步时,您基本上是在说此函数将返回一个 Promise,而当您在异步函数中使用 await 时,您基本上是在说此函数的其余部分将成为 .then 回调的一部分隐含的承诺。

回答你的问题,signIn() 被标记为异步,这意味着它返回一个承诺(这就是为什么signIn().then 不会抛出 TypeError),并且在其中你正在创建一个承诺 (auth.signInWithEmailAndPassword) ,但该承诺与signIn 的隐含承诺无关,因此隐含承诺立即解决。换句话说,您的signIn 的原始版本相当于:

signIn() {
  auth.signInWithEmailAndPassword(this.email,this.password).then((cred) => {
    console.log(cred);
    return cred;
  });
  return Promise.resolve(undefined);
}

要解决此问题,您需要从 signIn 中删除 async 关键字,并从 signIn 返回承诺,如果您想在它之外使用 .then。例如:

signIn() {
  return auth.signInWithEmailAndPassword(this.email,this.password).then((cred) => {
    console.log(cred);
    return cred;
  });
}

上面的代码其实等价于这个async/await版本:

async signIn() {
  const cred = await auth.signInWithEmailAndPassword(this.email,this.password);
  console.log(cred);
  return cred;
}

请注意,尽管在您的事件侦听器中,您并没有等待承诺,因此它将在 signIn 承诺完成之前解决。也就是说,你原来的事件处理器相当于:

e => {
  e.preventDefault();

  const user = new User(loginForm.email.value,loginForm.password.value);
  user.signIn().then(cred => { 
    console.log(cred);
    loginForm.reset();
  });
  return Promise.resolve(undefined);
}

可能对你没有任何意义,因为 DOM 事件要么不关心回调的返回值,要么尊重承诺。这是一个在某些情况下可能很重要的区别(例如,如果您在 .then / 在 async 之后调用它,e.preventDefault() 将不起作用)

【讨论】:

  • 太棒了。它现在有效,这很有意义,我需要返回承诺。只是为了确保我完全理解-到您的最后一点“请注意,尽管在您的事件侦听器中,您并没有等待承诺,因此它将在 signIn 承诺完成之前返回。”通过在我的 signIn 中返回承诺,我将有效地在我的事件监听器中等待这个承诺?
  • @user2954945 我已经更新了我的答案以提供更完整的解释。
  • 很棒的解释!非常感谢!我从中学到了很多。
猜你喜欢
  • 2019-09-25
  • 2019-12-11
  • 1970-01-01
  • 2019-08-18
  • 1970-01-01
  • 2018-06-26
  • 2014-06-14
  • 1970-01-01
  • 2019-10-07
相关资源
最近更新 更多