Thinking系列,旨在利用10分钟的时间传达一种可落地的编程思想或解决方案。
在 codereview 代码中,发现了这样的两种写法。
写法一:
async function fn () {
return await someAsyncReq()
}
写法二:
function fn () {
return someAsyncReq()
}
有哪些区别呢?从写法上,直观可以看到的是
- 写法一:返回的是执行结果(异步执行过程在 fn 函数内部)
- 写法二:返回的是 Promise(异步执行过程在**调用 fn **函数的方法)
我们知道,调用 async 包裹的函数也需要通过 async...await
进行处理;同样的获取异步结果,也可以通过 async...await
处理,那么上述调用方式一直:
await fn()
从这个层面看,貌似我们可以忽略具体返回是 retrun promise
还是 return await promise
。这个结论,在一定场景下的确没有问题 – 异步函数没有异常抛出
/*写法一:示例*/
async function fn1 () {
return await new Promise(reslove => setTimeout(() => reslove('ligang'), 1000))
}
/*写法二:示例*/
function fn2 () {
return new Promise(reslove => setTimeout(() => reslove('ligang'), 1000))
}
await fn1()
await fn2()
上述执行结果一致,都会在 1000ms 后返回 “ligang”。
那么什么场景下,上述两种写法会有差异呢?
写法一:
async function fn () {
try {
return await someAsyncReq()
} catch (err) {
return await Promise.rejct('异步操作发生错误')
}
}
写法二:
function fn () {
try {
return someAsyncReq()
} catch (err) {
return Promise.rejct('异步操作发生错误')
}
}
当异步操作发生异常时,会有差异:
- 写法一:会返回异常信息,即执行 catch 部分
- 写法二:异常的捕获需要在调用的函数中处理,fn 函数中 catch 部分无法执行
如果仔细分析,相信大家可以得出相应的结论。
function promisedDivision(n1, n2) {
if (n2 === 0) {
return Promise.reject(new Error('Cannot divide by 0'))
}
return Promise.resolve(n1 / n2)
}
/*写法一:示例*/
async function fn1 () {
try {
return await promisedDivision(6, 0)
} catch (err) {
return err
}
}
/*写法二:示例*/
function fn2 () {
try {
return promisedDivision(6, 0)
} catch (err) {
return err
}
}
await fn1() // Error: Cannot divide by 0
await fn2() // Uncaught Error: Cannot divide by 0
对于方式一,reject 的错误被成功捕获;对于方式二,reject 的错误被直接抛出了(Uncaught)。对于异常的处理,是提升代码鲁棒性的重要途径之一。且对错误未捕获,会导致程序终止执行。
结论
-
如果当前场景,需要我们对错误统一处理,建议使用写法一
return await someAsyncReq()
,在函数内部统一处理 -
如果当前场景,需要我们对错误差异化处理,建议使用写法二
return someAsyncReq()
,调用者可差异化处理
// return await promise
aysnc function _request(options) {
try {
return await axios(options)
} catch (err) {
let errorMsg = '请求发生了错误'
if (err.code === 'ECONNABORTED') {
errorMsg = '请求超时!'
} else if (axios.isCancel(err)) {
errorMsg = '请求被取消!'
} else {
switch (err.response.status) {
case 401: // token失效
case 403: // 无权限访问
case 404: // 接口不存在
case 500: // 接口错误
}
}
return await Promse.reject({
errorCode: err.response.status,
message: `${errorMsg}:${err}`
})
}
}
// return promise
async function request (options) {
return new Promise((resolve, reject) => {
try {
const {status, content} = _request(options)
if (status === 'success'){
resolve(content)
} else {
reject({
errorCode: status,
message: content
})
}
} catch (err) {
reject(err)
}
})
}
项目中对 axios 请求经常会按照上述原则进行封装,axios 请求我们直接 return await promise
处理,便于对统一错误进行通用性处理(如401、403、500等),一致性强,减少不必要的冗余代码;而对于业务端我们采用 return promise
处理,如 status !== 'success'
情况,将错误抛出由调用者根据业务情况进行差异化处理,灵活度更高,更能契合业务需求。