用async / await重写promise,寻求澄清

时间:2017-05-29 12:59:10

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

我有两个函数可以在我的前端处理Facebook登录/注销,而且这两个函数都是用promises编写的。我想用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语法吗?

3 个答案:

答案 0 :(得分:2)

我们使用关键字async来调用返回Promise个实例的函数。这些函数可以在其他await函数的主体中async

将回调样式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块。在此处包装异步函数的主体以捕获可能通过等待承诺而抛出的任何错误非常重要。

答案 1 :(得分:2)

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

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

由于您需要FB.login解析的值,因此您必须将其包装在Promise中,以便与async/await一起使用。

答案 2 :(得分:0)

由于使用FB.loginFB.logout函数的限制,恐怕您不能重写导出函数。原因是他们没有兑现承诺。但是,例如,如果FB进行了调整,并更改了函数的行为以返回可解析为response的Promise(如果response.authResponsefalse则抛出错误),则可以使用@Saravana在他的fiddle中提出的建议。

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

// 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.');
  }
}