如何避免在axios中发送多个重复的AJAX请求

时间:2019-04-26 13:08:19

标签: javascript reactjs axios redux-thunk

是否可以使用axios自动限制所有发往特定端点列表的请求?也许使用axios拦截器?

当前,我限制发送axios请求的用户操作,但是问题是我必须在有导致某些AJAX请求的用户操作的地方写这个。像这样

  const throttledDismissNotification = throttle(dismissNotification, 1000)

  const dismiss = (event: any) => {
    throttledDismissNotification();
  };

  render() {
    return (
      <Button onClick={dismiss}>Dismiss Notification</Button>
    )
  }

这导致很多混乱,我想知道是否可以自动化。

类似的东西:

if(request.url in listOfEndpointsToThrottle && request.params in cacheOfPreviousRequestsToThisEndpoint) {
  StopRequest();
}

很明显,这是伪代码,但是您知道了。

5 个答案:

答案 0 :(得分:4)

也许您可以尝试使用axios提供的Cancellation功能。

有了它,您可以确保在待处理状态下没有任何两个(或更多,取决于您的实现)类似的请求。

下面,您将找到一个简化的小示例,该示例演示如何确保仅处理最新请求。您可以对其进行一些调整以使其像请求池一样起作用

    import axios, { CancelToken } from 'axios';

    const pendingRequests = {};

    const makeCancellable = (headers, requestId) => {
      if (!requestId) {
        return headers;
      }

      if (pendingRequests[requestId]) {
        // cancel an existing request
        pendingRequests[requestId].cancel();
      }
      const source = CancelToken.source();
      const newHeaders = {
        ...headers,
        cancelToken: source.token
      };
      pendingRequests[requestId] = source;
      return newHeaders;
    };

    const request = ({
      url,
      method = 'GET',
      headers,
      id
    }) => {
      const requestConfig = {
        url,
        method,
        headers: makeCancellable(headers || {}, id)
      };

      return axios.request(requestConfig)
        .then((res) => {
          delete pendingRequests[id];
          return ({ data: res.data });
        })
        .catch((error) => {
          delete pendingRequests[id];
          if (axios.isCancel(error)) {
             console.log(`A request to url ${url} was cancelled`); // cancelled
          } else {
             return handleReject(error);
          }
        });
    };

    export default request;

答案 1 :(得分:2)

限制axios请求本身非常容易。真正令人头疼的是如何处理那些无效请求所返回的承诺。从无效的axios请求返回的那些诺言的合理行为是什么?他们应该永远待命吗?

我看不到任何完美的解决方案。但是后来我得出了一个有点作弊的解决方案:

  

如果我们不限制axios调用,而是限制实际的XMLHttpRequest怎么办?

这使事情变得更容易,避免了promise问题,并且易于实现。想法是为最近的请求实现缓存,如果新请求与最近的请求匹配,则只需从缓存中提取结果,然后跳过XMLHttpRequest。

由于采用how axios interceptors work的方式,以下构造可用于有条件地跳过某些XHR调用:

// This should be the *last* request interceptor to add
axios.interceptors.request.use(function (config) {
  /* check the cache, if hit, then intentionally throw
   * this will cause the XHR call to be skipped
   * but the error is still handled by response interceptor
   * we can then recover from error to the cached response
   **/ 
  if (requestCache.isCached(config)) {
    const skipXHRError = new Error('skip')
    skipXHRError.isSkipXHR = true
    skipXHRError.request = config
    throw skipXHRError
  } else {
    /* if not cached yet
     * check if request should be throttled
     * then open up the cache to wait for a response
     **/
    if (requestCache.shouldThrottle(config)) {
      requestCache.waitForResponse(config)
    }
    return config;
  }
});

// This should be the *first* response interceptor to add
axios.interceptors.response.use(function (response) {
  requestCache.setCachedResponse(response.config, response)
  return response;
}, function (error) {
  /* recover from error back to normalty
   * but this time we use an cached response result
   **/
  if (error.isSkipXHR) {
    return requestCache.getCachedResponse(error.request)
  }
  return Promise.reject(error);
});

答案 2 :(得分:1)

我有一个类似的问题,通过我的研究似乎缺少一个好的解决方案。我所看到的只是一些临时解决方案,所以我为axios开了一个问题,希望有人可以回答我的问题https://github.com/axios/axios/issues/2118

我也找到了这篇文章Throttling Axios requests,但是我没有尝试他建议的解决方案。

我正在讨论与此My implementation of debounce axios request left the promise in pending state forever, is there a better way?

答案 3 :(得分:0)

我完成了一个,@ hackape谢谢你的回答,代码如下:

const pendings = {}
const caches = {}
const cacheUtils = {
   getUniqueUrl: function (config) {

     // you can set the rule based on your own requirement
     return config.url + '&' + config.method
   },
   isCached: function (config) {
     let uniqueUrl = this.getUniqueUrl(config)
     return caches[uniqueUrl] !== undefined
   },
   isPending: function (config) {
     let uniqueUrl = this.getUniqueUrl(config)
     if (!pendings[uniqueUrl]) {
       pendings[uniqueUrl] = [config]
       return false
     } else {
       console.log(`cache url: ${uniqueUrl}`)
       pendings[uniqueUrl].push(config)
       return true
     }
   },
   setCachedResponse: function (config, response) {
     let uniqueUrl = this.getUniqueUrl(config)
     caches[uniqueUrl] = response
     if (pendings[uniqueUrl]) {
       pendings[uniqueUrl].forEach(configItem => {
         configItem.isFinished = true
       })
     }
   },
   getError: function(config) {
     const skipXHRError = new Error('skip')
     skipXHRError.isSkipXHR = true
     skipXHRError.requestConfig = config
     return skipXHRError
   },
   getCachedResponse: function (config) {
     let uniqueUrl = this.getUniqueUrl(config)
     return caches[uniqueUrl]
   }
 }
 // This should be the *last* request interceptor to add
 axios.interceptors.request.use(function (config) {

    // to avoid careless bug, only the request that explicitly declares *canCache* parameter can use cache
   if (config.canCache) {

     if (cacheUtils.isCached(config)) {
       let error = cacheUtils.getError(config)
       throw error
     }
     if (cacheUtils.isPending(config)) {
       return new Promise((resolve, reject) => {
         let interval = setInterval(() => {
           if(config.isFinished) {
             clearInterval(interval)
             let error = cacheUtils.getError(config)
             reject(error)
           }
         }, 200)
       });
     } else {

       // the head of cacheable requests queue, get the response by http request 
       return config
     }
   } else {
     return config
   }
 });

答案 4 :(得分:0)

非常方便的axios extensions https://www.npmjs.com/package/axios-extensions可以限制,缓存请求并自动重新发送失败的请求。