Grouping AJAX requests in a pool
Tags: javascript, ajax, performance, react
Published: 2018-09-10 08:53:47

---
title: Grouping AJAX requests in a pool
published: true
description: Our mission in this article is to improve the request time.
tags: javascript, ajax, performance, react
canonical_url: https://aralroca.com/2018/09/10/grouping-ajax-requests-in-a-pool/
cover_image: https://aralroca.files.wordpress.com/2018/09/portada.jpg?w=2560&h=1200&crop=1
---

*Original article: https://aralroca.com/2018/09/10/grouping-ajax-requests-in-a-pool/*

In this article I would like to explain what I did to improve the speed in the communication between client and server. It's important to understand that this is not a global solution for all AJAX request. Instead, it can only be applied in some particular type of request, as we will see soon if you keep reading.

Note that in most projects other solutions could be more efficient.

<h2>What's the initial problem?</h2>
I'm currently working in a complex React application where the user can mount their own interactive widgets by using React components. Some of these interactive widgets need to do some AJAX request to load / insert some data (or whatever) on componentDidMount, componentWillUnmount or more (as we will see soon).

To implement this first approach, we can make every interactive widget (React container) call the POST /whatever on componentDidMount method.

<img class="wp-image-110 aligncenter" src="https://aralroca.files.wordpress.com/2018/09/problem.png" alt="Problem.png" width="652" height="403" />
<p style="text-align: center;"><i><strong>Image1</strong></i><i>. In this example is POST /evaluate</i></p>
In this implementation, each container is the responsible of doing the corresponding POST /evaluate. Or, using Redux, each container is the responsible to dispatch an action that, in turn, will do the request. After resolving each promise, each container decides what to do with the evaluation.

At the beginning, in this example, is going to emit at least 5 requests at the same tick of the clock. And, after resolving these requests, React is going to change the DOM at least 5 times, in different renders.

This implementation can be enough quickly for some cases. However, remember that the user can mount their own page with a big amount of interactive widgets. So this means that 20, 30 or more request can be emitted at the same ticking.

Unfortunately, there is a limitation on how many requests we can emit at the same time, so the rest are added in a queue that increments the total time. Moreover, in this /evaluate we are evaluating the same things through different widgets (for example, the item "a" is evaluated 3 times in the <i>Image1)</i>.

Our mission in this article is to improve the request time by grouping all these requests into one, and removing duplicates.

<img class="wp-image-113 aligncenter" src="https://aralroca.files.wordpress.com/2018/09/clock.png" alt="clock.png" width="523" height="290" />
<h2></h2>
<h2>Type of request to group</h2>
Before starting the implementation, the first important step is to know which is the request target. We can't group every type of request, at least without modifying the behaviour on back-side.

How should the request be?
<ul>
	<li>It should accept an array as a parameter.</li>
	<li>The response is an array in the same order.</li>
	<li>If any item can't be resolved, instead of using a 500 Internal Server Error, the status should be 200 OK. The error should be in the response array index.</li>
	<li>Each item should spend approximately the same time to be resolved. If the evaluation of "a" is taking 10 times more than the evaluation of "f", this wouldn't be a good approach because we prefer to load each interactive widget independently.</li>
</ul>
<h2>Grouping AJAX requests in a container</h2>
After analysing the initial problem, a common solution we can apply, in order to improve the loading speed of the page, is using a parent container to group all the requests while removing the duplicated items.

This parent container in the componentDidMount method does this AJAX call (or uses a Redux Action to do that). Then, this parent container distributes the results to its children (or, using Redux, each children container gets their results from the store).

<img class="wp-image-111 aligncenter" src="https://aralroca.files.wordpress.com/2018/09/container-solution.png" alt="Container-Solution.png" width="641" height="396" />

In this way, instead of emitting 20 or 30 request at the same time, we group all these request into one. Also, after resolving the promise of the request, React is going to render the new DOM for all the interactive widgets at the same time.
<h2></h2>
<h2>More problems on the way...</h2>
In the above example we only took care about componentDidMount method. However, in reality, each interactive widget can have an "interval" property in the configuration. This widgets are able to send different requests on each "interval" tick.

<img class="aligncenter size-full wp-image-118" src="https://aralroca.files.wordpress.com/2018/09/interval1.gif" alt="interval.gif" width="1280" height="720" />

In this case we are having more troubles to group all requests emitted in each tick of the clock in the parent container. However, it's possible. In order to fix the problem we can create a common interval in the parent container with the greatest common divisor of all the children intervals. This global interval checks in every tick which requests need to emit in order to group them. Also, another alternative is to create different intervals on the parent container without time duplicates.

By the way, let me tell you something else: Some interactive widgets can be connected and the "interval" property can be changed depending on the output of another widget.

<img class="alignnone size-full wp-image-119" src="https://aralroca.files.wordpress.com/2018/09/interval2.gif" alt="interval2.gif" width="1280" height="720" />

More troubles... Still not impossible to group requests depending on each ticking by using a parent container, but maybe we need to<strong> re-think a painless and more flexible way to implement this</strong>.

<img class="wp-image-114 aligncenter" src="https://aralroca.files.wordpress.com/2018/09/kevin-ku-392517-unsplash.jpg" alt="kevin-ku-392517-unsplash.jpg" width="492" height="370" />
<h2></h2>
<h2>Grouping AJAX requests in a pool</h2>
A different way, instead of implementing all the logic of all the cases in the parent container, is to use an AJAX pool to directly group all the request emitted in the same ticking into only one request.

<img class="size-full wp-image-117 aligncenter" src="https://aralroca.files.wordpress.com/2018/09/ajax-pool.png" alt="AJAX-POOL.png" width="2202" height="1000" />

The pool is adding in a queue all things to evaluate emitted in the same ticking. In the next tick it will do the request by sending all the queue as param.

To use this pool, it's necessary that the interactive widgets use the corresponding service instead of sending directly the request.

Instead of:
<pre style="margin: 0; line-height: 125%;">axios.post(<span style="color: #a31515;">'/evaluate'</span>, { data: [a, b] })
.then(res => {
 <span style="color: #008000;">// ...</span>
})</pre>
Use:
<pre style="margin: 0; line-height: 125%;">EvaluationService.evaluate([a, b])
.then(res => {
 <span style="color: #008000;">// ...</span>
})</pre>
These promises always return the filtered result to each widget.

Each service will use an AJAX pool or not, depending on the type of the request. In this case in the EvaluationService we are going to use this pool.

This EvaluationService is the responsible of initialising the pool, adding the items into the queue, removing duplicates and saving the indexes. Then, when the request is resolved, it will filter the required items from the total response.
<pre style="margin: 0; line-height: 125%;"><span style="color: #0000ff;">import</span> AjaxPool from <span style="color: #a31515;">'./services/ajax-pool'</span>;

<span style="color: #0000ff;">const</span> pool = <span style="color: #0000ff;">new</span> AjaxPool();

<span style="color: #0000ff;">export</span> <span style="color: #0000ff;">default</span> <span style="color: #0000ff;">class</span> EvaluateService {
  <span style="color: #0000ff;">static</span> evaluate(data) {
    <span style="color: #0000ff;">const</span> id = pool.initPool();

    <span style="color: #0000ff;">const</span> indexes = data
      .map((item) => {
        <span style="color: #0000ff;">let</span> index = pool.findInQueue(id,
          existingItem => _.isEqual(existingItem, item),
        );

        <span style="color: #0000ff;">if</span> (index === -1) {
          index = pool.addToQueue(id, exp);
        }

        <span style="color: #0000ff;">return</span> index;
      });

    <span style="color: #0000ff;">return</span> pool
      .request(id, <span style="color: #a31515;">'/evaluate'</span>, queue => ({  data: queue }), <span style="color: #a31515;">'post'</span>)
      .then((allEvaluations) => indexes.map(index => allEvaluations[index]));
  }
}</pre>
Every time we call the evaluate method of this service, it first calls the initPool to get the corresponding "id" of the pool. This "id" is unique for each AJAX request. If there are more than one execution in the same tick of the clock, the same "id" should be used in all the group.

The purpose of the AJAX pool is to resolve all the promises of the group with the same response, but using just one AJAX request.
<pre style="margin: 0; line-height: 125%;"><span style="color: #008800; font-weight: bold;">import</span> uuid from <span style="background-color: #fff0f0;">'uuid'</span>;
<span style="color: #008800; font-weight: bold;">import</span> axios from <span style="background-color: #fff0f0;">'axios'</span>;

<span style="color: #008800; font-weight: bold;">const</span> DEFAULT_DELAY <span style="color: #333333;">=</span> <span style="color: #0000dd; font-weight: bold;">0</span>; <span style="color: #888888;">// Wait the next ticking</span>

<span style="color: #008800; font-weight: bold;">export</span> <span style="color: #008800; font-weight: bold;">default</span> <span style="color: #008800; font-weight: bold;">class</span> AjaxPool {
  constructor(milliseconds <span style="color: #333333;">=</span> DEFAULT_DELAY) {
    <span style="color: #008800; font-weight: bold;">this</span>.DELAY_MILLISECONDS <span style="color: #333333;">=</span> milliseconds;
    <span style="color: #008800; font-weight: bold;">this</span>.queues <span style="color: #333333;">=</span> {};
    <span style="color: #008800; font-weight: bold;">this</span>.needsInitialization <span style="color: #333333;">=</span> <span style="color: #008800; font-weight: bold;">true</span>;
    <span style="color: #008800; font-weight: bold;">this</span>.requests <span style="color: #333333;">=</span> {};
    <span style="color: #008800; font-weight: bold;">this</span>.numRequest <span style="color: #333333;">=</span> {};
  }

  <span style="color: #888888;">/**</span>
<span style="color: #888888;">   * Initialising the queue </span>
<span style="color: #888888;">   */</span>
   initPool() {
     <span style="color: #008800; font-weight: bold;">if</span> (<span style="color: #008800; font-weight: bold;">this</span>.needsInitialization) {
       <span style="color: #008800; font-weight: bold;">this</span>.requestID <span style="color: #333333;">=</span> uuid();
       <span style="color: #008800; font-weight: bold;">this</span>.queues[<span style="color: #008800; font-weight: bold;">this</span>.requestID] <span style="color: #333333;">=</span> [];
       <span style="color: #008800; font-weight: bold;">this</span>.needsInitialization <span style="color: #333333;">=</span> <span style="color: #008800; font-weight: bold;">false</span>;
       <span style="color: #008800; font-weight: bold;">this</span>.numRequest[<span style="color: #008800; font-weight: bold;">this</span>.requestID] <span style="color: #333333;">=</span> <span style="color: #0000dd; font-weight: bold;">0</span>;
     }

     <span style="color: #008800; font-weight: bold;">return</span> <span style="color: #008800; font-weight: bold;">this</span>.requestID;
   }

   findInQueue(id, method) {
     <span style="color: #008800; font-weight: bold;">if</span> (<span style="color: #008800; font-weight: bold;">typeof</span> method <span style="color: #333333;">!==</span> <span style="background-color: #fff0f0;">'function'</span>) {
       <span style="color: #008800; font-weight: bold;">return</span> <span style="color: #333333;">-</span><span style="color: #0000dd; font-weight: bold;">1</span>;
     }

     <span style="color: #008800; font-weight: bold;">return</span> _.findIndex(<span style="color: #008800; font-weight: bold;">this</span>.queues[id], method);
   }

   cleanRequest(id) {
     <span style="color: #008800; font-weight: bold;">this</span>.numRequest[id] <span style="color: #333333;">-=</span> <span style="color: #0000dd; font-weight: bold;">1</span>;

     <span style="color: #008800; font-weight: bold;">if</span> (<span style="color: #008800; font-weight: bold;">this</span>.numRequest[id] <span style="color: #333333;">===</span> <span style="color: #0000dd; font-weight: bold;">0</span>) {
       <span style="color: #008800; font-weight: bold;">delete</span> <span style="color: #008800; font-weight: bold;">this</span>.requests[id];
       <span style="color: #008800; font-weight: bold;">delete</span> <span style="color: #008800; font-weight: bold;">this</span>.queues[id];
       <span style="color: #008800; font-weight: bold;">delete</span> <span style="color: #008800; font-weight: bold;">this</span>.numRequest[id];
     }
   }

   <span style="color: #888888;">/**</span>
<span style="color: #888888;">    * Add to queue</span>
<span style="color: #888888;">    * </span>
<span style="color: #888888;">    * @param {any} queueElement </span>
<span style="color: #888888;">    * @return {number} index of element on the queue</span>
<span style="color: #888888;">    */</span>
   addToQueue(id, queueElement) {
     <span style="color: #008800; font-weight: bold;">return</span> <span style="color: #008800; font-weight: bold;">this</span>.queues[id].push(queueElement) <span style="color: #333333;">-</span> <span style="color: #0000dd; font-weight: bold;">1</span>;
   }

   request(id, url, getData, method <span style="color: #333333;">=</span> <span style="background-color: #fff0f0;">'get'</span>) {
     <span style="color: #008800; font-weight: bold;">this</span>.numRequest[id] <span style="color: #333333;">+=</span> <span style="color: #0000dd; font-weight: bold;">1</span>;
     <span style="color: #008800; font-weight: bold;">return</span> <span style="color: #008800; font-weight: bold;">new</span> Promise((res, rej) <span style="color: #333333;">=></span> {
       _.delay(() <span style="color: #333333;">=></span> {
         <span style="color: #008800; font-weight: bold;">this</span>.needsInitialization <span style="color: #333333;">=</span> <span style="color: #008800; font-weight: bold;">true</span>;

         <span style="color: #008800; font-weight: bold;">if</span> (<span style="color: #333333;">!</span><span style="color: #008800; font-weight: bold;">this</span>.requests[id]) {
           <span style="color: #008800; font-weight: bold;">const</span> data <span style="color: #333333;">=</span> <span style="color: #008800; font-weight: bold;">typeof</span> getData <span style="color: #333333;">===</span> <span style="background-color: #fff0f0;">'function'</span> <span style="color: #333333;">?</span> getData(<span style="color: #008800; font-weight: bold;">this</span>.queues[id]) <span style="color: #333333;">||</span> {} <span style="color: #333333;">:</span> {};
           <span style="color: #008800; font-weight: bold;">this</span>.requests[id] <span style="color: #333333;">=</span> axios[method](url, data);
         }
         <span style="color: #888888;">// For each request in the same "ticking" is doing one AJAX </span>
         <span style="color: #888888;">// request, but all resolve the same promise with the same result</span>
         <span style="color: #008800; font-weight: bold;">this</span>.requests[id]
           .then((result) <span style="color: #333333;">=></span> {
             <span style="color: #008800; font-weight: bold;">if</span> (result.error) {
               rej(result.error);
             } <span style="color: #008800; font-weight: bold;">else</span> {
               res(result);
             }

             <span style="color: #008800; font-weight: bold;">this</span>.cleanRequest(id);
           })
           .<span style="color: #008800; font-weight: bold;">catch</span>((err) <span style="color: #333333;">=></span> {
             <span style="color: #008800; font-weight: bold;">this</span>.cleanRequest(id);
             rej(err);
           });
       }, <span style="color: #008800; font-weight: bold;">this</span>.DELAY_MILLISECONDS);
     });
   }
}</pre>
In this case we won't use a big delay, it's just going to be 0 milliseconds to wait the next ticking. However, it's possible to use some milliseconds as a param to construct the pool. For example, if we use 100ms, it will group more requests.
<pre style="margin: 0; line-height: 125%;"><span style="color: #0000ff;">const</span> pool = <span style="color: #0000ff;">new</span> AjaxPool(100);</pre>

<h2><img class=" size-full wp-image-126 aligncenter" src="https://aralroca.files.wordpress.com/2018/09/ajax-pool.gif" alt="ajax-pool.gif" width="800" height="386" /></h2>
📕 <strong>Code</strong>: <a href="https://stackblitz.com/edit/ajax-pool">https://stackblitz.com/edit/ajax-pool</a>
<h2></h2>
<h2>Conclusion</h2>
Grouping requests in a pool:
<ul>
	<li>Improves the total loading time in Client, avoiding the addition of some requests in a queue.</li>
	<li>The server has less requests, reducing costs.</li>
	<li>It's reusable and every component of the project can use it without extra logic.</li>
</ul>
Although:
<ul>
	<li>It's not always the best solution, only for a specific type of requests.</li>
</ul>
<img class="wp-image-122 aligncenter" src="https://aralroca.files.wordpress.com/2018/09/matt-lamers-769327-unsplash.jpg" alt="matt-lamers-769327-unsplash.jpg" width="476" height="476" />