async
函数
ES2017
引入了async
函数,使得异步操作和使用更加便捷。可以像写同步代码似的书写异步代码。
// 常规 Promise 写法
fetchPageData()
.then((pageDataResponse) => {
const { data } = pageDataResponse;
setPageData(data);
});
// 使用 async 写法
const getPageData = async () => {
const pageDataResponse = await fetchPageData();
const { data } = pageDataResponse;
setPageData(data);
};
错误处理
虽然async
函数使得异步操作简单化,但是其出现异常时,错误处理却是比较麻烦的。
try...catch
错误处理:将await
错误包裹在try...catch
代码块中,进行异常捕获。
const getPageData = async () => {
try {
const pageDataResponse = await fetchPageData();
const { data } = pageDataResponse;
setPageData(data);
} catch (e) {
console.log('page data fetch error ', e);
}
};
promise reject catch
错误处理:async
函数返回一个Promise
对象,其内部抛出错误,会导致返回的Promise
对象变为reject
状态,可以通过catch
函数进行错误兜底。
const getPageData = async () => {
const pageDataResponse = await fetchPageData().catch((e) => {
console.log('page data fetch error ', e);
});
const { data } = pageDataResponse;
setPageData(data);
};
针对单个async
函数使用这些错误处理还是可行,当有多个async
函数需要处理时,就不是很优雅了。尤其是当针对每一个async
函数的错误处理还不一致时,对于有代码洁癖的人可能就到了不能忍的程度了。
const getDisplayData = async () => {
try {
const data1 = await fetchData1();
const data2 = await fetchData2();
...
} catch (e) {
console.log('error catch ', e);
}
};
await-to-js 错误处理
文章 How to write async await without try-catch blocks in Javascript 对如何在javascript
中优雅的使用async
和await
进行错误的处理?有详细的描述,其中有推荐 await-to-js 这个库,也可以通过npm
安装的方式进行使用。其代码也比较简单,async
函数返回的是一个promise
对象,对promise
对象进行一个统一的wrap
处理,其wrap
就包含catch
异常处理。
/**
* @param { Promise } promise
* @param { Object= } errorExt - Additional Information you can pass to the err object
* @return { Promise }
*/
export function to<T, U = Error> (
promise: Promise<T>,
errorExt?: object
): Promise<[U, undefined] | [null, T]> {
return promise
.then<[null, T]>((data: T) => [null, data])
.catch<[U, undefined]>((err: U) => {
if (errorExt) {
Object.assign(err, errorExt);
}
return [err, undefined];
});
}
export default to;
使用 await-to-js
进行异步操作。
import to from 'await-to-js';
const fetchData1 = async () => {};
const fetchData2 = async () => {};
const fetchPageData = async () => {
const data1 = to(fetchData1());
const data2 = to(fetchData2());
const [error1, res1] = data1;
const [error2, res2] = data1;
if (res1) {}
if (error1) {}
if (res2) {}
if (error2) {}
};
进而可以对 await-to-js
进行二次封装,进行业务使用。
import to from 'await-to-js';
const awaitWrap = async <T, U = Error>(
promise: Promise<T>,
onSuccess?: (data: T) => void,
onError?: () => void,
errorExt?: object,
) => {
const handleError = onError || defaultErrorFn;
const handleSuccess = onSuccess || defaultSuccessFn;
const res = await to<T, U>(promise, errorExt);
const [data, error] = res;
onSuccess(data);
onError(error);
};
使用awaitWrap
进行异步操作。
const fetchData1 = async () => {
await awaitWrap(
fetch('/data1'),
(data) => {
setData1(data);
},
(error) => {
const { message } = error;
toast.error(message);
},
);
};
const fetchData2 = async () => {
await awaitWrap(
fetch('/data2'),
(data) => {
setData2(data);
},
(error) => {
const { message } = error;
toast.error(message);
},
);
};
const fetchPageData = async () => {
await fetchData1();
await fetchData2();
...
};
综上,async await
异常处理不仅是try...catch
,通过对async await
异常处理进行提取封装,也可以使得异常处理更加的优雅。