如何避免在 axios 中发送多个重复的 AJAX 请求
How to avoid sending multiple duplicate AJAX requests in axios
是否可以使用 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();
}
显然这是伪代码,但你明白了。
也许您可以尝试使用 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;
我也有类似的问题,研究了一下好像没有好的解决办法。我看到的只是一些临时解决方案,所以我为 axios 打开了一个问题,希望有人能回答我的问题 https://github.com/axios/axios/issues/2118
我也找到了这篇文章Throttling Axios requests,但我没有尝试他建议的解决方案。
我有一个与此相关的讨论
限制 axios 请求本身非常容易。真正令人头疼的是如何处理从无效请求返回的承诺。在处理从无效的 axios 请求返回的承诺时,什么被认为是 理智的行为 ?他们应该永远悬而未决吗?
我没有看到这个问题的完美解决方案。但后来我想到了一个有点作弊的解决方案:
What if we don't throttle the axios call, instead we throttle the actual XMLHttpRequest?
这让事情变得更简单,因为它避免了 promise 问题,而且更容易实现。这个想法是为最近的请求实现一个缓存,如果一个新的请求匹配最近的一个请求,你只需从缓存中提取结果并跳过 XMLHttpRequest。
由于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 normality
* but this time we use an cached response result
**/
if (error.isSkipXHR) {
return requestCache.getCachedResponse(error.request)
}
return Promise.reject(error);
});
我完成了一个,@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
}
});
是否可以使用 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();
}
显然这是伪代码,但你明白了。
也许您可以尝试使用 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;
我也有类似的问题,研究了一下好像没有好的解决办法。我看到的只是一些临时解决方案,所以我为 axios 打开了一个问题,希望有人能回答我的问题 https://github.com/axios/axios/issues/2118
我也找到了这篇文章Throttling Axios requests,但我没有尝试他建议的解决方案。
我有一个与此相关的讨论
限制 axios 请求本身非常容易。真正令人头疼的是如何处理从无效请求返回的承诺。在处理从无效的 axios 请求返回的承诺时,什么被认为是 理智的行为 ?他们应该永远悬而未决吗?
我没有看到这个问题的完美解决方案。但后来我想到了一个有点作弊的解决方案:
What if we don't throttle the axios call, instead we throttle the actual XMLHttpRequest?
这让事情变得更简单,因为它避免了 promise 问题,而且更容易实现。这个想法是为最近的请求实现一个缓存,如果一个新的请求匹配最近的一个请求,你只需从缓存中提取结果并跳过 XMLHttpRequest。
由于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 normality
* but this time we use an cached response result
**/
if (error.isSkipXHR) {
return requestCache.getCachedResponse(error.request)
}
return Promise.reject(error);
});
我完成了一个,@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
}
});