用 async/await 重写承诺,寻求澄清
Rewriting promises with async/await, seeking for clarification
我的前端有两个处理 Facebook login/logout 的函数,它们都是用 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 语法吗?
我们使用关键字 async
来调用 return 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
已经采用这种形式。它 returns 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
块。将异步函数的主体包装在其中非常重要,以捕获等待承诺可能引发的任何错误。
您不需要将 FB.logout
函数包装在 Promise 包装器中的原因是您没有使用 FB.logout
的解析值,而您基本上是 "firing and forgetting"。您可以编写相同的方法而不像下面那样具有相同的效果:
export function logout(): void {
try {
FB.logout((response) => {
// no-op
})
} catch (err) {
throw err
}
}
并且因为您需要 FB.login
解析的值,您必须将其包装在 Promise
中才能与 async/await
一起使用。
由于 FB.login
和 FB.logout
函数的使用限制,恐怕您无法重写导出函数。原因是他们没有 return 承诺。但是,如果例如 FB 进行了调整,并将函数行为更改为 return 解析为 response
的承诺(或者如果 response.authResponse
为 false
则抛出错误),那么您可以使用@Saravana 在他的 fiddle.
中建议的那个
所以目前,尝试重写您当前的 login
和 logout
函数只会引入更多代码。你能做的就是让它们保持那样(因为它们那样没问题),当你在某处调用函数时,你可以等待它们,因为你知道它们已经 return 一个承诺。例如
// 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.');
}
}
我的前端有两个处理 Facebook login/logout 的函数,它们都是用 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 语法吗?
我们使用关键字 async
来调用 return 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
已经采用这种形式。它 returns 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
块。将异步函数的主体包装在其中非常重要,以捕获等待承诺可能引发的任何错误。
您不需要将 FB.logout
函数包装在 Promise 包装器中的原因是您没有使用 FB.logout
的解析值,而您基本上是 "firing and forgetting"。您可以编写相同的方法而不像下面那样具有相同的效果:
export function logout(): void {
try {
FB.logout((response) => {
// no-op
})
} catch (err) {
throw err
}
}
并且因为您需要 FB.login
解析的值,您必须将其包装在 Promise
中才能与 async/await
一起使用。
由于 FB.login
和 FB.logout
函数的使用限制,恐怕您无法重写导出函数。原因是他们没有 return 承诺。但是,如果例如 FB 进行了调整,并将函数行为更改为 return 解析为 response
的承诺(或者如果 response.authResponse
为 false
则抛出错误),那么您可以使用@Saravana 在他的 fiddle.
所以目前,尝试重写您当前的 login
和 logout
函数只会引入更多代码。你能做的就是让它们保持那样(因为它们那样没问题),当你在某处调用函数时,你可以等待它们,因为你知道它们已经 return 一个承诺。例如
// 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.');
}
}