我有一个呼叫端点以获取一些资源,后端人员建议尝试呼叫3次,每次呼叫之间延迟5秒。所以我创建了下面的函数…
const timeout = delay => {
return new Promise(resolve => setTimeout(resolve, delay))
}
const retry = useRef(0)
const fetchAPIResource = useCallback(
async () =>
await getStatus(transactionID)
.then(async res => {
if (res?.status === 204) {
await timeout(5000)
fetchAPIResource()
}
if (!res?.data?.receipt_is_settled && retry.current !== 3) {
await timeout(5000)
fetchAPIResource()
retry.current += 1
}
if (res?.data?.receipt_is_settled) {
setStatus('accepted')
setShow(false)
}
if (!res?.data?.receipt_is_settled && retry.current === 3) {
setStatus('declined')
setShow(false)
}
})
.catch(error => {
toast.error(error?.errors?.description || error?.description)
setStatus('declined')
}),
[],
)
我现在的问题是在最后一个调用执行的一瞬间,这个条件(!res?.data?.receipt_is_settled && retry.current === 3)
为真,因此错误屏幕显示在成功屏幕之前,我需要帮助使这更好,所以我只得到错误屏幕或成功屏幕。
…此条件(!res?.data?)receipt_is_settled,,重试。Current === 3)为true,因此错误屏幕显示在成功屏幕之前…
这告诉我你使用retry.current
作为你的渲染逻辑的一部分。因为它不在状态中(或纯粹从状态派生),所以不应该这样做。
我将使错误状态显式状态,并使重试计数器局部的useCallback
回调。没有理由让该信息在处理重试逻辑的代码之外可用。
重试成功的(status = 200)请求似乎也很奇怪。
还要注意,将async
/await
与.then
/.catch
混合会使代码难以遵循,并且
我试图重新编写代码,以提供一个示例,您可以如何做到这一点,但代码提出了很多问题:
res
真的可以为空吗?- 当获得
status
而不是204
时,res.data
真的可以为空吗? - 当出现错误时,
error
真的可以为空吗? - 为什么有些事情算作重试,而其他事情不算作重试? 为什么计数器没有复位?
所以这是我的看法,做出假设:
- 不需要重试已成功的操作。
- 所有执行超时和重试的分支都应算作重试。
- 除200或204之外的任何状态都是失败的,我们不应该重试
const fetchAPIResource = useCallback(async () => {
try {
for (let retries = 0; retries < 3; ++retries) {
const res = await getStatus();
if (res.status === 200 && res.data.receipt_is_settled) {
// All good
setStatus("accepted");
setShow(false);
return;
}
if (res.status !== 200 && res.status !== 204) {
throw new Error(`HTTP error ${res.status}`);
}
// Retry
await timeout(5000);
}
// Out of retries
setStatus("declined");
setShow(false);
} catch (error) {
// *** Can `error` really be nullish?
// *** Shouldn't there be a default if both of the below are `undefined`?
toast.error(error?.errors?.description || error?.description);
setStatus("declined");
// *** Shouldn't there be a `setShow(false);` here?
}
}, []);
上面没有显示:如果组件在这15秒内卸载,该进程可能应该被取消。
javascript按顺序运行代码,在你的代码中,你把
if (!res?.data?.receipt_is_settled && retry.current !== 3) {
await timeout(5000)
fetchAPIResource()
retry.current += 1
}
之前if (!res?.data?.receipt_is_settled && retry.current === 3) {
setStatus('declined')
setShow(false)
}
所以条件retry.current === 3
在第二次试验后总是为真。解决这个问题的方法是,您可以尝试输入以下代码:
if (!res?.data?.receipt_is_settled && retry.current === 3) {
setStatus('declined')
setShow(false)
}
if (!res?.data?.receipt_is_settled && retry.current !== 3) {
await timeout(5000)
fetchAPIResource()
retry.current += 1
}