我正在尝试编写一个将由反应组件使用的承诺。我正在使用
lodash
进行节流。
这个想法是使用这个函数的组件应该尽可能多地透明地调用这个函数,而不会导致太多调用。
let throttledApi = throttle(() => {
// tracking number of attempts
let attempts = 0;
const retryableFn = () => {
// do I need a return before api here?
api()
.then((response) => {
return response;
})
.catch((error) => {
attempts++;
if (attempts === 3) throw error; // throw to the component using this functino
// same question as above for below line
else retryableFn();
});
};
}, 60 * 1000); // 1 minute
// I am using it like this
throttledFn = throttledApi();
// How do I properly use the throttled instance
// How do I try/catch properly.
// Is this thenable?
throttledFn();
有几个问题,但即使解决了这些问题,主要问题仍然是节流不是为了这个目的。它旨在处理以不可预测(通常受用户影响)的方式随时间传播的单独调用。
首先列出其他问题:
retryableFn
永远不会被调用。您传递给 throttle
的函数仅定义了该函数,但随后对其不执行任何操作。
内部调用
retryableFn
绕过节流,直接调用函数,使节流无用
throttledApi
是节流函数,所以做 throttledFn = throttledApi()
实际上是执行该节流函数并将其返回值(即 undefined
)分配给 throttledFn
。你应该只使用 throttledApi
而忘记 throttledFn
出于您的目的,您不需要节流,而是需要延迟的循环。你可以用
setTimeout
来实现,最优雅的是先承诺setTimeout
:
// Generic function
const delay = (ms) => new Promise(resolve => setTimeout(resolve, ms));
// Generic function
const retryAsync = (asyncFunc, maxNumAttempts, timeout) => {
return asyncFunc().catch(async (error) => {
if (--maxNumAttempts <= 0) throw error;
await delay(timeout);
return retryAsync(asyncFunc, maxNumAttempts, timeout);
});
}
// Specific function (mock)
const api = async function () {
console.log("Api called");
await delay(100);
if (Math.random() < .9) {
console.log("...throw");
throw "api doesn't like this game.";
}
console.log("You're lucky today!");
return { data: [1, 2, 3] };
}
// Run it
retryAsync(api, 3, 2_000).then((response) => {
console.log("The response is: ", response);
}).catch((error) => {
console.log("Giving up. The error is: ", error)
});