【问题标题】:Rewriting promises with async/await, seeking for clarification用 async/await 重写 promise,寻求澄清
【发布时间】:2017-10-29 19:12:46
【问题描述】:

我的前端有两个处理 Facebook 登录/注销的函数,它们都是用 Promise 编写的。我想用async/await重写这些。

带有承诺的原始代码:

export function login() {
  return new Promise<facebookSdk.IAuthResponse>((resolve, reject) => {
    FB.login((response) => {
      if (response.authResponse) {
        resolve(response)
      } else {
        reject(errors.facebookLoginError)
      }
    })
  })
}

export function logout() {
  return new Promise<facebookSdk.IAuthResponse>((resolve) => {
    FB.logout((response) => {
      resolve(response)
    })
  })
}

这是我使用async/await 编写它们的方式:

function promiseFacebookLogin(): Promise<facebookSdk.IAuthResponse | Error> {
  return new Promise<facebookSdk.IAuthResponse>((resolve, reject) => {
    FB.login((response) => {
      if (response.authResponse) {
        resolve(response)
      } else {
        reject(errors.facebookLoginError)
      }
    })
  })
}

export async function login(): Promise<facebookSdk.IAuthResponse | Error> {
  try {
    return await promiseFacebookLogin()
  } catch (err) {
    throw err
  }
}

export async function logout(): Promise<facebookSdk.IAuthResponse | void> {
  try {
    return await FB.logout((response) => {
      return response
    })
  } catch (err) {
    throw err
  }
}

生成的代码按预期工作,但我想澄清一些事情。如您所见,我可以摆脱logout 函数的整个promise 语法。但是,当涉及到 login 函数时,我无法这样做。我不得不单独包装承诺并等待结果。据我研究,这似乎是回调函数的常见做法。

有人能解释一下为什么不能在 login 函数上完全摆脱 Promise 语法吗?

【问题讨论】:

  • 你不能写你的login方法like this来消除承诺吗?我不确定这是否有效。
  • 这是 FB.login 的签名:login(callback: (response: IAuthResponse) =&gt; void): void 没有回调我不能调用它。
  • 认为FB.login 会通过查看FB.logout 来返回一个承诺。如果FB.logout 没有返回承诺,你怎么能从logout 方法返回任何东西?它不会总是返回void吗?
  • 好点,它确实返回void,但它按预期完成了logout操作。
  • 这可能是真的,因为您没有使用您的承诺返回的值,因为您的 logout 方法将在您的回调执行之前返回。如果你需要回调中的值,那么你也必须用一个 Promise 包装 FB.logout

标签: javascript typescript async-await es6-promise facebook-sdk-4.x


【解决方案1】:

您不需要在 Promise 包装器中包装 FB.logout 函数的原因是因为您没有使用 FB.logout 的解析值,您基本上是在“解雇和忘记”。您可以编写相同的方法而无需像下面这样具有相同的效果:

export function logout(): void {
  try {
    FB.logout((response) => {
      // no-op
    })
  } catch (err) {
    throw err
  }
}

因为您需要FB.login 解析的值,所以您必须将其包装在Promise 中才能与async/await 一起使用。

【讨论】:

    【解决方案2】:

    我们使用关键字async 来调用返回Promise 实例的函数。此类函数可以在其他 async 函数的主体中 awaited。

    将回调样式 API 转换为基于 Promise 的常见模式:

    function fbLogin(..., cb) { ... }
    
    async function genFbLogin(...args): Promise<*> {
      return new Promise((resolve, reject) => {
        fbLogin(...args, (error, result) => {
          if (error) {
            reject(error);        
          } else {
            resolve(result);
          }
        });
      });
    }
    

    如您所见,您的示例中的login 已经采用这种形式。它返回Promise,因此您可以在其声明之前放置async 关键字。与logout 相同。这些已经是基于 Promise 而不是基于回调的。那么logout函数的正确使用方法如下:

    async function myLogoutHandler() {
      try {
        const response = await logout();
        console.log(response); // do sth with the result      
      } catch (e) {
        console.log('error', e);
      }
    }
    

    注意try-catch 块。将异步函数的主体包装在其中以捕获等待承诺可能引发的任何错误非常重要。

    【讨论】:

    • 如果我错了,请纠正我;您的fbLogin 示例在同一代码块中返回承诺,而我的只是将相同的代码块包装在另一个函数中。它们都以相同的方式工作。我的问题不同:在logout 中,我们根本不需要Promise 关键字。但是我们都需要在fbLogin 函数中使用Promise 关键字。这是为什么?我正在寻找的解释是这些函数之间的区别。
    • 如果您await 不是Promise,它只会解析为该表达式的值。例如。 console.log(await undefined),日志undefined。这就是你的情况。你的logout返回Promise,但是这个promise会立即解决,与FB.logout的实际处理无关。
    • 我们与logout 在同一页面上,对于login,我们必须返回一个承诺。你能在你的回答中解释这两种情况的区别吗?
    • 每个async函数默认返回Promise。您不需要像使用 login 那样隐式执行此操作。
    • 我试过了,但在那种情况下你不能打电话给resolvereject,可以吗?我什至尝试返回async 函数并在回调中调用Promise.resolve() + Promise.reject(),它不能作为隐式返回一个承诺。
    【解决方案3】:

    由于FB.loginFB.logout 函数的使用限制,恐怕你无法重写导出函数。原因是他们不返回承诺。但是,如果例如 FB 进行了调整,并更改了函数行为以返回解析为 response 的承诺(或者如果 response.authResponsefalse 则抛出错误),那么您可以使用 @Saravana 在他的fiddle.

    所以目前,尝试重写您当前的loginlogout 函数只会引入更多代码。你可以做的是让它们保持这种状态(因为它们可以这样),当你在某个地方调用函数时,你可以等待它们,因为你知道它们已经返回了一个 Promise。例如

    // at some distant part of your code base
    async function someFunc() {
      try:
        // await the response which is the one resolved in the callback function
        // inside the FB.login call
        let fbLoginResponse = await login();
    
        // ... do something with the response here ...
    
      catch (err) {
        // the error here is errors.facebookLoginError because
        // it's the one rejected in the callback function in the FB.login call
        console.log('You are not authenticated! Please try again.');
      }
    }
    
    

    【讨论】:

      猜你喜欢
      • 2021-04-27
      • 1970-01-01
      • 2018-01-22
      • 1970-01-01
      • 2023-03-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多