Skip to content Skip to sidebar Skip to footer

Delay Batch GET Requests To Server, JavaScript

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 meth

Solution 1:

The problem with your code is that you only call the fulfil method of the first promise in getUrl, but call that many times. The promise will be resolved when the first call to fulfil occurs, subsequent calls will be ignored.

Using a chained promise queue is a simple way to schedule the calls:

function delay(ms, val) {
    return new Promise(resolve => {
        setTimeout(() => {
            resolve(val);
        }, ms);
    });
}

class Scheduler { 
    constructor(interval) {
        this.queue = Promise.resolve();
        this.interval = interval;
    }
    submit(fn) {
        const result = this.queue.then(fn);
        this.queue = this.queue.then(() => delay(this.interval));
        return result;
    }
    wrapFn(fn) {
        const _this = this;
        return function() {
            const targetThis = this, targetArgs = arguments;
            console.log("Submitted " + arguments[0]); // for demonstration
            return _this.submit(() => fn.apply(targetThis, targetArgs));
        }
    }
}

function mockGet(url) {
    console.log("Getting " + url);
    return delay(100, "Resolved " + url);
}
const scheduler = new Scheduler(500);
const getUrl = scheduler.wrapFn(mockGet);

getUrl("A").then(x => console.log(x));
getUrl("B").then(x => console.log(x));
getUrl("C").then(x => console.log(x));
setTimeout(() => {
    getUrl("D").then(x => console.log(x));
    getUrl("E").then(x => console.log(x));
    getUrl("F").then(x => console.log(x));

}, 3000);

EDIT: Above snippet was edited to wait for a constant amount of time instead of the last mockGet resolution. Although I don't really understand your goals here. If you have a single client, then you should be fine with serializing the api calls. If you hav lots of clients, then this solution will not help you, as the same amount of calls will be made in a time segment as without this throttling, just the load on your server will be spread instead of coming in bursts.

EDIT: Made the code object oriented to be simpler to modularize to meet some your clean code requirements.


Solution 2:

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 !


Post a Comment for "Delay Batch GET Requests To Server, JavaScript"