首页 > 解决方案 > setInterval 有条件地调用两个异步函数

问题描述

我遇到了两个异步函数的问题。它们旨在从 API 顺序获取登录链接,但有时 Web 服务尚未准备好,因为它依赖于物理设备(可以重置),有时初始渲染发生在服务启动之前。

authConfigURL因此,如果未填充or ,则间隔每 2 秒运行一次两个异步函数loginURL,这不可能是最佳的,至少在当前状态下不是。

最初我有useEffect()这两个函数并调用它们,将它们传递loginURLauthConfigURL依赖数组。如果未填充 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);
    }
  }

标签: javascriptreactjs

解决方案


这篇文章着眼于在允许用户登录之前重试失败的尝试从服务器获取“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}并将处理它们留到承诺链的后面。


推荐阅读