javascript - 如何在异步/等待函数捕获错误时重试?
问题描述
我对 JS 和一般编程相当陌生,在尝试将 try/catch 实现到我的异步函数中时遇到了以下问题。它看起来像这样:
(async function main() {
try {
await myFunc();
} catch (e) {
console.log(e)
}
setTimeout(main, 2000);
}) ();
如您所见,它通过 setTimeout 调用对其自身进行循环,使其无限...除非它捕获错误。自然,我想尝试在 e 的某些实例中重试 myFunc,但不知道如何在不删除 setTimeout 的情况下实现它(这里 2 秒的延迟很重要)。
我尝试使用标签:
let critErrCount;
(async function main() {
critErrCount = 0;
lbl:
try {
await myFunc();
} catch (e) {
if (e instanceof errType1) {
console.log(`errType1: ${e}`);
continue lbl;
} else if (e instanceof errType2) {
critErrCount += 1;
console.log(`errType2: ${e}`);
console.log(`critErrCount: ${critErrCount}`);
if (critErrCount == 5) {
console.log(`Too many critErrs, aborting...`)
process.exit;
}
continue lbl;
} else {
process.exit
}
}
setTimeout(main, 2000);
}) ();
果然,它不起作用,因为据我所知,“继续”的行为不像其他语言中的“goto”,只能在适当的循环中工作(forEach、while、for 等)。由于由于 nodejs 运行时细节(显示为“非法继续语句”),catch 块内的 setTimeout 也不起作用,所以我没有想法。
解决方案
您可以使用重试机制,它可以让您重试异步函数调用所需的次数。在这篇博文中,我写了如何做到这一点,但这里是实现:
/**
* Execute a promise and retry with exponential backoff
* based on the maximum retry attempts it can perform
* @param {Promise} promise promise to be executed
* @param {function} onRetry callback executed on every retry
* @param {number} maxRetries The maximum number of retries to be attempted
* @returns {Promise} The result of the given promise passed in
*/
function retry(promise, onRetry, maxRetries) {
// Notice that we declare an inner function here
// so we can encapsulate the retries and don't expose
// it to the caller. This is also a recursive function
async function retryWithBackoff(retries) {
try {
// Make sure we don't wait on the first attempt
if (retries > 0) {
// Here is where the magic happens.
// on every retry, we exponentially increase the time to wait.
// Here is how it looks for a `maxRetries` = 4
// (2 ** 1) * 100 = 200 ms
// (2 ** 2) * 100 = 400 ms
// (2 ** 3) * 100 = 800 ms
const timeToWait = 2 ** retries * 100;
console.log(`waiting for ${timeToWait}ms...`);
await waitFor(timeToWait);
}
return await promise();
} catch (e) {
// only retry if we didn't reach the limit
// otherwise, let the caller handle the error
if (retries < maxRetries) {
onRetry();
return retryWithBackoff(retries + 1);
} else {
console.warn('Max retries reached. Bubbling the error up')
throw e;
}
}
}
return retryWithBackoff(0);
}
/**
* Wait for the given milliseconds
* @param {number} milliseconds The given time to wait
* @returns {Promise} A fulfiled promise after the given time has passed
*/
function waitFor(milliseconds) {
return new Promise((resolve) => setTimeout(resolve, milliseconds));
}
以下是如何使用该retry
助手:
/** Fake an API Call that fails for the first 3 attempts
* and resolves on its fourth attempt.
*/
function generateFailableAPICall() {
let counter = 0;
return function () {
if (counter < 3) {
counter++;
return Promise.reject(new Error("Simulated error"));
} else {
return Promise.resolve({ status: "ok" });
}
};
}
/*** Testing our Retry with Exponential Backoff */
async function test() {
const apiCall = generateFailableAPICall();
const result = await retry(
apiCall,
() => {
console.log("onRetry called...");
},
4
);
console.log("result: ", result);
}
test();
推荐阅读
- excel - 我在使用 Excel VBA 创建 Pivot 时出错
- android - 计算数字的百分比
- python - 一个函数,它接受一个列表值并返回一个字符串,其中所有项目由逗号和空格分隔,并在最后一项之前插入
- msbuild - -t:go 标志在 MSBuild 中的作用是什么?
- database - 如何创建多个 NoSQL 存储并访问它们(Flutter/Dart)?
- excel - Excel 公式垂直返回值
- python - 使用 Plotly 绘制子图条形图
- php - 在 PHP 中比较和组合 2 个数组
- python - 编写函数时是否有必要(在 Python 2.7 中)为全局变量创建参数?
- tableau-api - 在画面中查找总计