延迟批量GET请求到服务器,JavaScript [英] Delay batch GET requests to server, JavaScript

查看:166
本文介绍了延迟批量GET请求到服务器,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
});
};

这是我所追踪的算法:


  1. 每次调用 getUrl 时,我将参数保存在列表中。

  2. 然后,我检查一下 setInterval 计时器是否已经启动。




    • 如果没有,那么我以给定的延迟开始,否则我什么都不做。


  3. 执行时, 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);
//代码中的随机位置中的一吨其他电话

只有第一个响应是打印,其他的没有。



问题




  1. 我做错了什么?

  2. 如何改善这个代码?


解决方案

解决方案



延迟批处理请求与延迟任何异步调用完全相同。经过很多尝试和询问,我终于来到了我在这个问题上寻找的解决方案:





深入解释了理由,为什么我选择了我的答案。



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:

  1. Every time I call getUrl, I save the parameters in a list.
  2. 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.
  3. 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.

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

  1. What am I doing wrong?
  2. 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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆