延迟批量GET请求到服务器,JavaScript [英] Delay batch GET requests to server, JavaScript
问题描述
背景
我正在向服务器发送一批HTTP GET请求,我需要调整它们,以避免杀死不良服务器。为了我的演示的目的,这将是GET方法:
/ *
*此函数模拟一个真实的HTTP GET请求,总是需要1秒
*给出响应。在这种情况下,总是给出相同的回应。
* /
let mockGet = function(url){
return new Promise(fulfill => {
setTimeout(
url => {
履行({url,data:banana});
},
1000,url);
});
};
我在几个地方使用了 mockGet
并且在不同的上下文中,所以现在我想使用 getUrl
函数,使用 mockGet
,但是将其限制到合理的步伐
我的意思是我需要一个函数,多次调用将始终以给定的顺序执行,但在不同执行之间给定的延迟。
研究
我的第一个尝试是使用像 underscorejs
和 lodash
实现这一点:
但它很少,因为他们没有提供我的功能。
事实证明,我需要将每个调用保存在列表中,所以稍后可以在方便时调用它。按照这个逻辑,我发现另一个答案:
哪个答案我的问题,但有几个问题,我打算修复。它具有全局变量,不分离关注点,迫使用户知道内部机制...我想要一些更清晰的东西,下划线或lodash之间的一切隐藏了一个易于使用的功能。
代码
我承认这个挑战是使用Promises的工厂模式,以返回我需要的:
let getFactory = function(args){
let {
throttleMs
} = ARGS;
let argsList = [];
让processTask;
/ *
*每次调用此函数时,我将url参数添加到
*参数列表中。那么当时间到了,我拿出最旧的参数
*我运行mockGet函数,有效地做了一个队列。
* /
let getUrl = function(url){
argsList.push(url);
return new Promise(fulfill => {
if(processTask === undefined){
processTask = setInterval(()=> {
if(argsList.length === 0){
clearInterval(processTask);
processTask = undefined;
}
else {
let arg = argsList。 shift();
满足(mockGet(arg));
}
},throttleMs);
}
});
};
return Object.freeze({
getUrl
});
};
这是我所追踪的算法:
- 每次调用
getUrl
时,我将参数保存在列表中。 -
然后,我检查一下
setInterval
计时器是否已经启动。
- 如果没有,那么我以给定的延迟开始,否则我什么都不做。
-
执行时,
setInterval
函数检查参数队列。
- 如果为空,我将停止
setInterval
计时器。 - 如果它有参数,我从列表中删除第一个,并使用真实的
mockGet
函数及其结果履行Promise。
- 如果为空,我将停止
问题
尽管所有的逻辑似乎都是到目前为止,这还不行。当我使用它时,会发生什么:
/ *
*所有调用其任何功能的分离X ms,并将
*全部按照被调用的顺序执行。
* /
让throttleFuns = getFactory({
throttleMs:5000
});
throttleFuns.getUrl('http://www.bananas.pt')
.then(console.log);
throttleFuns.getUrl('http://www.fruits.es')
.then(console.log);
throttleFuns.getUrl('http://www.veggies.com')
.then(console.log);
//代码中的随机位置中的一吨其他电话
只有第一个响应是打印,其他的没有。
问题
- 我做错了什么?
- 如何改善这个代码?
解决方案
延迟批处理请求与延迟任何异步调用完全相同。经过很多尝试和询问,我终于来到了我在这个问题上寻找的解决方案:
深入解释了理由,为什么我选择了我的答案。
Queue vs Math
在上面的答案中,我比较了解决这个问题的方法。一个使用队列逻辑,像我正在尝试在这里,一个使用数学。通常如果你有一个数学表达式,你最终需要更少的逻辑,代码更容易维护。这是我选择的解决方案。
//将我们的最后一个电话输入值
let lastCall = Date。现在();
let delayAsync = function(url){
return new Promise(fulfill => {
//延迟至少`delayMs`,但如果需要,最后一次调用更多
const now = Date.now();
const thisDelay = Math.max(delayMs,lastCall - now + 1 + delayMs);
lastCall = now + thisDelay;
setTimeout(() => {
//使用'asyncMock'的承诺
履行(asyncMock(url));
},thisDelay);
}完成我们的承诺;
};
但是,由于我是使用队列逻辑的路径,所以我决定把我的2美分嗯,我很自豪。
有关更多信息,我强烈建议您阅读整个事情!
Background
I am making a batch of HTTP GET requests to a server and I need to throttle them to avoid killing the poor server. For the purposes of my demo, this will be the GET method:
/*
* This function simulates a real HTTP GET request, that always takes 1 seconds
* to give a response. In this case, always gives the same response.
*/
let mockGet = function(url) {
return new Promise(fulfil => {
setTimeout(
url => {
fulfil({ url, data: "banana"});
},
1000, url);
});
};
I am using the mockGet
in several places and in different contexts, so now I want to instead use a getUrl
function that uses mockGet
but that throttles it to a reasonable pace.
By this I mean I need a function that when called multiple times will always execute in the given order, but with the given delay between different executions.
Research
My first tentative was to use libraries like underscorejs
and lodash
to achieve this:
but it fell short, as they don't provide the functionality I am after.
Turns out, I need to save each invocation in a list, so I can later call it when it is convenient. Following this logic I found another answer:
Which kinda answer my questions, but has several problems I plan to fix. It has global variables, no separation of concerns, forces the user to know the inner mechanics ... I want something cleaner, something among the lines of underscorejs or lodash that hides all that behind an easy to use function.
Code
My take on this challenge was to use the factory pattern with Promises in order to return what I needed:
let getFactory = function(args) {
let {
throttleMs
} = args;
let argsList = [];
let processTask;
/*
* Every time this function is called, I add the url argument to a list of
* arguments. Then when the time comes, I take out the oldest argument and
* I run the mockGet function with it, effectively making a queue.
*/
let getUrl = function(url) {
argsList.push(url);
return new Promise(fulfil => {
if (processTask === undefined) {
processTask = setInterval(() => {
if (argsList.length === 0) {
clearInterval(processTask);
processTask = undefined;
}
else {
let arg = argsList.shift();
fulfil(mockGet(arg));
}
}, throttleMs);
}
});
};
return Object.freeze({
getUrl
});
};
This is the algorithm I am following:
- Every time I call
getUrl
, I save the parameters in a list. Then, I check to see if the
setInterval
timer has started.- If it has not, then I start with with the given delay, otherwise I do nothing.
Upon executing, the
setInterval
function checks for the queue of arguments.- If it is empty, I stop the
setInterval
timer. - If it has arguments, I remove the first one from the list, and I fulfil the Promise with the real
mockGet
function and with its result.
- If it is empty, I stop the
Problem
Although all the logic seems to be in place, this is not working ... yet. What happens is that when I use it:
/*
* All calls to any of its functions will have a separation of X ms, and will
* all be executed in the order they were called.
*/
let throttleFuns = getFactory({
throttleMs: 5000
});
throttleFuns.getUrl('http://www.bananas.pt')
.then(console.log);
throttleFuns.getUrl('http://www.fruits.es')
.then(console.log);
throttleFuns.getUrl('http://www.veggies.com')
.then(console.log);
// a ton of other calls in random places in code
Only the first response is printed, and the other ones are not.
Questions
- What am I doing wrong?
- How can I improve this code?
Solution
Delaying batch requests is exactly the same as delaying any asynchronous call. After a lot of tries and asking around, I finally came to the solution I was looking for in this question:
There is an in depth explanation of the reasoning and why I picked the answer I did as well.
Queue vs Math
In the answer above, I compare to solution to this problem. One using queue logic, like I was attempting to do here, and one using Math.
Usually if you have a mathematic expression you end up needing less logic and the code is easier to maintain. This is the solution I picked.
// Seed our "last call at" value
let lastCall = Date.now();
let delayAsync = function(url) {
return new Promise(fulfil => {
// Delay by at least `delayMs`, but more if necessary from the last call
const now = Date.now();
const thisDelay = Math.max(delayMs, lastCall - now + 1 + delayMs);
lastCall = now + thisDelay;
setTimeout(() => {
// Fulfill our promise using the result of `asyncMock`'s promise
fulfil(asyncMock(url));
}, thisDelay);
});
};
However, since I was on the path of using queue logic, I decided to post my 2 cents as well, and I am quite proud of it.
For more information I strongly recommend you read the whole thing !
这篇关于延迟批量GET请求到服务器,JavaScript的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!