javascript - setInterval 有条件地调用两个异步函数
问题描述
我遇到了两个异步函数的问题。它们旨在从 API 顺序获取登录链接,但有时 Web 服务尚未准备好,因为它依赖于物理设备(可以重置),有时初始渲染发生在服务启动之前。
authConfigURL
因此,如果未填充or ,则间隔每 2 秒运行一次两个异步函数loginURL
,这不可能是最佳的,至少在当前状态下不是。
最初我有useEffect()
这两个函数并调用它们,将它们传递loginURL
给authConfigURL
依赖数组。如果未填充 URL,我无法确定以特定时间间隔运行请求的正确方法。
如果不满足条件或请求的响应为空,是否有更好的方法来处理调用函数?
function useInterval(callback, delay) {
const savedCallback = useRef();
useEffect(() => {
savedCallback.current = callback;
});
useEffect(() => {
function tick() {
savedCallback.current();
}
let id = setInterval(tick, delay);
return () => clearInterval(id);
}, [delay]);
}
useInterval(() => {
if (!authConfigURL || !loginURL) {
setIsWaiting(true);
fetchRootResources();
fetchAuthenticationConfig();
}
}, 2000);
async function fetchRootResources() {
const rootURL = "/admin/api/rest/";
try {
const response = await fetch(rootURL, {
method: "GET",
headers: { Accept: "blah"},
});
if (!response.ok) {
console.error("Handle response codes", response);
setErrorMessage("Error fetching Root REST API Resources");
}
// if the response contains no data this will error out
const data = await response.json();
// set rootResources here
setRootResources({
time: data.links.time,
// 10 or so other links
});
// this URL is needed in the next async function
setAuthConfigURL(data.links.auth_config);
} catch (error) {
console.log("Error fetching root resources", error);
}
}
async function fetchAuthenticationConfig() {
// authConfigURL seems to be empty on initial render
console.log("fetching authConfig with url: ", authConfigURL);
try {
const response = await fetch(authConfigURL, {
method: "GET",
headers: { Accept: "blah" },
});
if (!response.ok) {
console.error("Handle response codes", response);
}
const data = await response.json();
console.log(data);
setLoginURL(data.data.links.login);
} catch (error) {
console.log("Error fetching auth config", error.message);
}
}
解决方案
这篇文章着眼于在允许用户登录之前重试失败的尝试从服务器获取“root”和“授权”数据的替代方法。
在概念上,连续尝试两个服务器请求,如果任何一个请求超时、失败或返回错误数据,则从头开始重试请求。重试逻辑支持在不使用间隔计时器的情况下设置最大重试次数和重试间隔时间。
处理服务器中断的最佳方法或在短时间内重试失败操作的实际好处等更广泛的问题没有被刻意考虑。
在这种情况下,可以使用各种技术和编码风格来提供错误处理。要改进工作代码或讨论个别方法的优点,请考虑在Code Review上发布以获取反馈。
更新:包括从服务器获取响应的超时(编码为
Promise.race([ fetch-request, expiry-timer])
匹配间隔计时器的行为。
回应评论的一个例子:
"use strict";
const getRootResources = rootURL => fetch(rootURL, {
method: "GET",
headers: { Accept: "blah"}
});
const getAuthConfig = authConfigURL => fetch(authConfigURL, {
method: "GET",
headers: { Accept: "blah" }
});
const ResponseError = new Error("Fetch response timeout");
const RootError = new Error("Error fetching Root REST API Resources");
const AuthError = new Error("Failed to obtain config data from server");
const RetryError = new Error("An error occurred connecting to server, please retry again later");
const setupLogin = async () => {
const rootURL = "https://www.example.com/admin/api/rest/";
const maxRetry = 3;
const retryMsec = 2000;
const delay = async msec=> new Promise( resolve=>setTimeout(resolve, msec));
const responseMsec = 2000;
const expire = (msec,reason)=> new Promise((resolve,reject)=> setTimeout(reject, msec, reason));
let rootData;
let authData;
for( var tryAgain = maxRetry; tryAgain; --tryAgain) {
console.log("tries remaining: %s", tryAgain);
let response = null;
rootData = null;
authData = null;
try {
response = await Promise.race( [
getRootResources( rootURL),
expire(responseMsec, ResponseError)
]);
if (!response.ok) {
console.error("Handle response codes", response);
throw RootError;
}
rootData = await response.json(); // may throw
const authConfigURL = rootData.links.auth_config;
response = await Promise.race( [
getAuthConfigURL( authConfigURL),
expire(responseMsec, ResponseError)
]);
if (!response.ok) {
console.error("Handle response codes (config)", response);
throw AuthError;
}
authData = await response.json();
break;
}
catch(err) {
console.error("Fetch or getting json failed");
console.log(err);
await delay(retryMsec);
continue;
}
}
if( tryAgain <= 0) {
throw RetryError;
}
return {rootData, authData};
}
window.addEventListener("DOMContentLoaded", event => {
setupLogin()
.then( ({rootData, authData}) => {
console.log("setupLogin() succeeded");
// enable page login features:
// setRootResources( rootData); // or as needed
// setAuthConfig( authData); // or as needed
// ... blah
})
.catch( err=> {
console.log("setupLogin() failed");
console.error(err);
// disable page login features and notify user
});
});
Run to test that code compiles and reports failures to reach server.
请注意,它setupLogin
返回一个承诺{rootData, authData}
并将处理它们留到承诺链的后面。
推荐阅读
- python - 尝试将项目添加到模型时出现 Django FileNotFoundError
- npm - 是否可以将具有 ssr 模式的 Nuxt 应用程序作为 npm 包发布?(带有服务器中间件)
- python - 在odoo(python)中继承时如何更改日期格式?
- reactjs - NEXTJS 将表单数据发送到 api 端点 - 无响应
- reactjs - matomo-tracker-react useMatomo 返回“无效的钩子调用”
- reactjs - 如何打印通过上下文 api 传递的数组的特定元素?
- laravel - Stripe、Stripe Payments、Connect、关联账户、拆分支付
- python - client.get_symbol_info 返回无 Python
- python - 与 SymPy 的三个球体相交(三边测量)
- c# - C# linq 表达式。如何获得不同的偏移量和“任何”偏移量?