【问题标题】:let only last API call get through只让最后一个 API 调用通过
【发布时间】:2020-10-26 07:16:49
【问题描述】:

所以,我有这个输入框,当您在其中输入内容时,它会对我们的后端进行 API 调用,我遇到的问题是:

假设我们正在输入测试,我将对此进行 4 次调用:

  • t
  • tes
  • 测试

因为 't' 的调用比 'test' 需要更长的时间,所以来自 't' 的数据将在最后加载。这意味着我没有从“测试”获得请求的数据。

我的问题是,有什么方法可以取消之前的请求? ('t', 'te', 'tes') 并且只让你的最后一个电话打通?或者这只是优化 API 速度的性能?

我已经尝试过半秒超时,但有时问题仍然存在。

【问题讨论】:

  • 你已经标记了这个axios,那么你有没有在 Axios 中查看如何取消请求?
  • 如前所述,它被称为“去抖动”。基本上,您为每次更改设置了一个超时(假设为 500 毫秒),并且在此超时结束时,您正在检查值是否未更改,然后才通过 API 请求。在线阅读更多内容,我确信 react 内置了一些 dobounce 机制。
  • @vladkatz 不是,我会尽快添加答案
  • @HMR 同意他请求的不是去抖动,但他请求的行为是去抖动可以带来更好的性能和更少的 API 调用
  • @vladkatz 查看答案,去抖动不会始终解决根据用户输入发出异步请求:the order the async results resolve may not be the order the user gave the inputs

标签: javascript reactjs api axios


【解决方案1】:

使用 JavaScript 的 AbortController。它是专门为取消网络请求而实现的。

看看这个https://developer.mozilla.org/en-US/docs/Web/API/AbortController

【讨论】:

    【解决方案2】:

    当您对用户输入进行异步调用时,异步结果解析的顺序可能不是用户输入的顺序(因此不是去抖动问题)。当用户键入s 时,您使用s 进行异步调用,然后用户键入e,您使用se 进行异步调用。现在有 2 个异步调用未解决,一个是 s,一个是 se

    假设s 调用需要一秒钟,se 调用需要 10 毫秒,然后se 首先解析并且 UI 设置为 se 的结果,但之后 s 解析并且 UI 设置为s 的结果。您现在的 UI 不一致。

    解决此问题的一种方法是使用 debounce,希望您永远不会收到持续时间超过 debounce 时间的异步调用,但不能保证。另一种方法是取消较旧的请求,但实现起来太痛苦了,而且并非所有浏览器都支持。我在下面展示的方式只是在发出新请求时拒绝异步承诺。因此,当用户键入 se 请求 sse 但当 sse 之后解析时,它将被拒绝,因为它已被更新的请求替换。

    const REPLACED = 'REPLACED';
    const last = (fn) => {
      const current = { value: {} };
      return (...args) => {
        const now = {};
        current.value = now;
        return Promise.resolve(args)
          .then((args) => fn(...args))
          .then((resolve) =>
            current.value === now
              ? resolve
              : Promise.reject(REPLACED)
          );
      };
    };
    const later = (value, time) =>
      new Promise((resolve) =>
        setTimeout(() => resolve(value), time)
      );
    const apiCall = (value) =>
      //slower when value length is 1
      value.length === 1
        ? later(value, 1000) //takes a second
        : later(value, 100); //takes 100ms
    const working = last(apiCall);
    const Api = ({ api, title }) => {
      const [value, setValue] = React.useState('');
      const [apiResult, setApiResult] = React.useState('');
      React.useEffect(() => {
        api(value).then((resolve) => {
          console.log(title, 'resolved with', resolve);
          setApiResult(resolve);
        });
      }, [api, title, value]);
      return (
        <div>
          <h1>{title}</h1>
          <h3>api result: {apiResult}</h3>
          <input
            type="text"
            value={value}
            onChange={(e) => setValue(e.target.value)}
          />
        </div>
      );
    };
    const App = () => (
      <div>
        <Api
          api={apiCall}
          title="Broken (type 2 characters fast)"
        />
        <Api api={working} title="Working" />
      </div>
    );
    ReactDOM.render(<App />, document.getElementById('root'));
    <script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>
    <div id="root"></div>

    【讨论】:

    • FWIW,SWR 库已经进行了相同的重复数据删除;它在内部保存最后一次请求的时间戳,并且只接受带有相同时间戳标记的响应:github.com/vercel/swr/blob/…
    • @HMR,您能否在代码中提供一些信息,说明什么是什么?虽然这是我需要的,但这对我来说是全新的,所以我真的不明白它在做什么。
    • @Yorbjörn last 函数是一个 curried function,它关闭了一个返回承诺 (apiCall) 的函数,并将调用该函数,但只有当它是来自的承诺时才解析承诺最后一次调用:The way I show below is just to reject the async promise when a newer request was made
    • 好的,我理解了柯里化函数的概念,但对我来说不太清楚的是我如何用它来实现我的 GET 请求。所以假设我需要从这个链接https://${api_uri}/name/${value} 获取我的数据,我在这段代码中将url 放在哪里,在哪里可以看到它的响应。对不起,如果我问愚蠢的问题
    • @Yorbjörn 假设你的 get 请求是由一个名为 apiCall 的函数完成的,那么 last(apiCall) 将返回一个函数,只有在最后调用它时才会解析,所以 lastApiCall = last(apiCall)lastApiCall(url).then...
    猜你喜欢
    • 2010-12-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-01-14
    相关资源
    最近更新 更多