Rewriting promises with async/await, seeking for clarification

1.7k Views Asked by At

I have two functions that handle Facebook login/logout on my front-end and both of them are written with promises. I want to rewrite these using async/await.

The original code with promises:

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)
    })
  })
}

Here's how I wrote them using 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
  }
}

The resulting code works as intended but there's something I want to clarify. As you can see, I can get rid of the entire promise syntax for logout function. However when it comes to the login function, I was unable to do so. I had to wrap the promise separately and await the result of that. As far as I researched, this seems to be common practice for callback taking functions.

Can somebody shed some light on why is it not possible to get rid of the Promise syntax completely on the login function?

3

There are 3 best solutions below

0
On BEST ANSWER

The reason you don't need to wrap the FB.logout function in a Promise wrapper is because you are not using the resolved value of FB.logout which you are basically "firing and forgetting". You could write the same method without like below to have the same effect:

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

And because you need the value resolved by FB.login, you have to wrap it in a Promise to use it with async/await.

5
On

We use keyword async to call functions that return Promise instances. Such functions can be awaited in body of other async functions.

There is a common pattern to turning callback style APIs to Promise-based:

function fbLogin(..., cb) { ... }

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

As you can see login from your example is already in this form. It returns Promise and therefore you can place async keyword before in its declaration. Same with logout. These already are Promise-based not callback-based. The correct way to use logout function is then the following:

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

Note the try-catch block. It's really important to wrap body of your async functions in this to catch any errors that can be thrown by awaiting promises.

0
On

I'm afraid that you can't rewrite the export functions because of the limitations from the usage of FB.login and FB.logout functions. The reason is that they don't return a promise. But if for example FB adapted, and changed the functions behavior to return a promise that resolves to the response (or throw an error if response.authResponse is false), then you can use the one suggested by @Saravana in his fiddle.

So for the moment, trying to rewrite your current login and logout functions will just introduce more code. What you can do is to keep them like that (because they are okay like that) and when you call the functions somewhere, you can await them because you know that they already return a promise. e.g.

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