如何在 async/await 函数捕获错误时重试?
How to retry upon async/await function catching error?
我对 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);
}) ();
当然,它没有用,因为据我了解,'continue' 在其他语言中的行为与 'goto' 不同,并且只能在适当的循环中工作(forEach、while、等)。由于 nodejs 运行时的具体情况(显示为 'Illegal continue statement'),catch 块内的 setTimeout 也不起作用,因此我没有想法。
您可以使用重试机制,让您可以根据需要重试异步函数调用的次数。 In this blogpost,我写了如何做到这一点,但这里是实现:
/**
* 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();
我对 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);
}) ();
当然,它没有用,因为据我了解,'continue' 在其他语言中的行为与 'goto' 不同,并且只能在适当的循环中工作(forEach、while、等)。由于 nodejs 运行时的具体情况(显示为 'Illegal continue statement'),catch 块内的 setTimeout 也不起作用,因此我没有想法。
您可以使用重试机制,让您可以根据需要重试异步函数调用的次数。 In this blogpost,我写了如何做到这一点,但这里是实现:
/**
* 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();