只让最后一个 API 调用通过

IT技术 javascript reactjs api axios
2021-04-16 10:15:04

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

假设我们正在打字测试,我将使用它进行 4 次调用:

  • 测试
  • 测试

因为't' 的调用比'test' 花费的时间长得多,'t' 的数据将在最后加载。这意味着我没有从“测试”中获得请求的数据。

在此处输入图片说明

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

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

2个回答

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

假设s调用需要一秒钟,se调用需要 10 毫秒,然后se首先解析并且 UI 设置为 result ofse但之后s解析并且 UI 设置为 result of s您现在有一个不一致的用户界面。

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

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>

@HMR,您能否在代码中提供一些关于什么的信息?尽管这是我需要的,但这对我来说是全新的,所以我真的不明白它在做什么。
2021-05-23 10:15:04
@Yorbjörn 该last函数是一个柯里化函数,它关闭一个返回Promise(apiCall)的函数,并将调用该函数,但仅当它是上次调用时产生的Promise时才解析该Promise:The way I show below is just to reject the async promise when a newer request was made
2021-05-30 10:15:04
好的,我理解柯里化函数的概念,对我来说不太清楚的是我如何用它实现我的 GET 请求。因此,假设我需要从此链接获取我的数据https://${api_uri}/name/${value},将 url 放在此代码中的何处,以及在哪里可以看到此响应。对不起,如果我问愚蠢的问题
2021-06-01 10:15:04
@Yorbjörn说你的GET请求是由一个名为函数来完成apiCall,然后last(apiCall)会返回一个函数,如果它是最后调用那样只会解决lastApiCall = last(apiCall)lastApiCall(url).then...
2021-06-09 10:15:04
FWIW,SWR 库已经做了同样的重复数据删除;它在内部保存最后一次请求的时间戳,并且只接受标记有相同时间戳的响应:github.com/vercel/swr/blob/...
2021-06-21 10:15:04

使用 JavaScript 的 AbortController。它专门用于取消网络请求。

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