跳到主要内容

关于错误处理

在开发中不可避免的会出现一些错误或异常,所以我们必须合理的 抛出捕获处理这些错误,让程序在我们的预期下正常运行。

抛出错误

对于一些网络请求相关的 Promise 函数,由于网络等各种原因,失败是非常常见的,但是对于这种情况,不能简单的直接抛出错误。

而应该是尝试了一定次数后依然失败后,再将错误抛出。

Promise.retry

所以我们需要实现一个 [Promise.retry](成功后 resolve 结果,失败后重试,尝试超过一定次数才真正的 reject) ,成功后 resolve 结果,失败后重试,尝试超过一定次数才真正的 reject。

Promise.retry = (fn, times = 3) => {
new Promise(async (resolve, reject) => {
while (times--) {
try {
const res = await fn()
console.log('执行成功结果是' + res)
break
} catch (err) {
console.log('执行失败:' + err)
}
}
reject('全部执行失败')
}).catch(err => {
console.log(err)
})
}

// 模拟异步函数
function getData() {
const n = Math.random()
return new Promise((resolve, reject) => {
setTimeout(() => {
if (n > 0.7) resolve(n)
else reject(n)
}, 1000)
})
}

//执行
Promise.retry(getData, 3)

控制台执行失败示例输出:

执行失败:0.4864293324880926
执行失败:0.1974050790106614
执行失败:0.595516781548987
全部执行失败

控制台执行成功示例输出:

执行失败:0.5607170295539814
执行成功结果是 0.8403887523046449

演示完后,实际 Promise.retry 可以这样写:

Promise.retry = function (promiseFn, times = 3) {
return new Promise(async (resolve, reject) => {
while (times--) {
try {
const res = await promiseFn()
resolve(res)
break
} catch {}
}
reject('retry failed')
})
}

捕获错误

为了捕获到错误,在代码中就避免不了的要写大量的 try...catch 结构,这样的弊端也显而易见,例如不利于代码阅读。

这篇文章 使用的方案很不错,我个人比较喜欢:

try...catch 的写法:

async function run() {
try {
console.log('start')
const result = await UnSuccess()
console.log('result:', result)
} catch (err) {
console.log('发生了错误!')
console.log(err)
}
console.log('end')
}
run()

then-catch 解构赋值:

async function run() {
console.log('start')

const [err, result] = await UnSuccess()
.then(result => [null, result])
.catch(err => [err, null])

if (err) {
console.log('发生了错误!')
console.log(err)
return
}

console.log('result:', result)
console.log('end')
}

run()

还可以对 then-catch 解构赋值 进行封装:

/**
* Promise函数错误处理
* @param {Function} asyncFn 这是一个Promise函数
* @returns {Array} [err,result]
*/
function AsyncHandling(asyncFn) {
return asyncFn()
.then(result => [null, result])
.catch(err => [err, null])
}

async function run() {
console.log('start')
const [err, result] = await AsyncHandling(UnSuccess)
if (err) {
console.log('发生了错误!')
console.log(err)
return
}
console.log('result:', result)
console.log('end')
}
run()

这样代码看起来就十分优雅了。

最后还要注意:

如果在 “计划的(scheduled)”代码中发生异常,例如在 setTimeout 中,则 try...catch 不会捕获到异常:

try {
setTimeout(function () {
noSuchVariable // 脚本将在这里停止运行
}, 1000)
} catch (err) {
alert("won't work")
}

因为这时引擎已经离开了 try...catch 结构。如果要捕获计划的函数中的异常,则需要将 try...catch 放在该函数内。

参考资料:

https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Statements/try...catch#%E7%A4%BA%E4%BE%8B

https://blog.imlete.cn/article/async-await-error-handling.html

https://zh.javascript.info/try-catch

https://blog.imlete.cn/

https://github.com/Advanced-Frontend/Daily-Interview-Question/issues/387

https://zh.javascript.info/promise-error-handling

本文参考并引述了上述链接中的部分相关内容,在这里做出感谢!