JavaScript, React - sending multiple simultaneous ajax calls - Stack Overflow

In my React application I have an array of parameters (some IDs, for example), which should be used as

In my React application I have an array of parameters (some IDs, for example), which should be used as a parameters for an ajax call queue. The problem is that array might contain more than 1000 items and if I just recursively make the ajax call using the forEach loop, the browser page eventually stops responding before each of the requests are resolved.

Is there a technique/library, which could allow to send ajax requests, for example, by 5 requests at a time asynchronously, and only when those are finished, proceed the next 5?

Follow up questions:

React - Controlling multiple Ajax Calls

React - Controlling AJAX calls made to the server

In my React application I have an array of parameters (some IDs, for example), which should be used as a parameters for an ajax call queue. The problem is that array might contain more than 1000 items and if I just recursively make the ajax call using the forEach loop, the browser page eventually stops responding before each of the requests are resolved.

Is there a technique/library, which could allow to send ajax requests, for example, by 5 requests at a time asynchronously, and only when those are finished, proceed the next 5?

Follow up questions:

React - Controlling multiple Ajax Calls

React - Controlling AJAX calls made to the server

Share edited Sep 26, 2017 at 17:43 ThinkGeek asked Aug 31, 2017 at 18:43 ThinkGeekThinkGeek 5,15716 gold badges50 silver badges101 bronze badges
Add a ment  | 

4 Answers 4

Reset to default 4

Ok, let's sort some things out. In JavaScript AJAX requests are asynchronous by nature. You chose to make them synchronous somewhat in you implementation.

What you need to do is have some array of requests, from which you pop results X at a time, wait for them to return, and repeat.

let ids = [a lot of ids here]

while (ids.length > 0) {

   let c = ids.splice(0, 5)
   let promises = []
   for (let i = 0; i < c.length; i++) {
      promises.push(fetch("someURL").then(function() {}))
   }
   Promise.all(promises)
}

Will execute 5 requests simultaneously, wait for them to plete, then fetch next portion of IDs

if you are not constrained by es version and can use es6 then you should look into async await

async function makeBatchCalls(arrayIds, length) {
    let test = arrayIds.reduce(
        (rows, key, index) => (index % length == 0 
            ? rows.push([key]) 
            : rows[rows.length - 1].push(key)
        ) && rows, 
        []
    );

    let Batchresults = [];

    //convert them to two dimensionl arrays of given length [[1,2,3,4,5], [6,7,8,9,10]]
    for (calls of test) {
       Batchresults.push(
           await Promise.all(
               calls.map((call) => fetch(`https://jsonplaceholder.typicode./posts/${call}`))
           )
       );
    }

    return Promise.all(Batchresults); //wait for all batch calls to finish
}

makeBatchCalls([1,2,3,4,5,6,7,8,9,10,12,12,13,14,15,16,17,18,19,20],3)

async/await is meant for the exact scenario of awaiting async calls. basically inside the async function until await is resolved the execution is suspended. You would need to understand promises and generators before you start using them.

I had the same problem in a project. What you need is a priority queue in order to control how many request will be performed simultaneously. I'm using this library. Since the p-queue implementation is simple enough to understand and is not that big, I've pasted the code in the snippet below only to show you how it works in the latest lines.

// IMPLEMENTATION ####################

// Port of lower_bound from http://en.cppreference./w/cpp/algorithm/lower_bound
// Used to pute insertion index to keep queue sorted after insertion
function lowerBound(array, value, p) {
  let first = 0;
  let count = array.length;

  while (count > 0) {
    const step = (count / 2) | 0;
    let it = first + step;

    if (p(array[it], value) <= 0) {
      first = ++it;
      count -= step + 1;
    } else {
      count = step;
    }
  }

  return first;
}

class PriorityQueue {
  constructor() {
    this._queue = [];
  }

  enqueue(run, opts) {
    opts = Object.assign({
      priority: 0
    }, opts);

    const element = {
      priority: opts.priority,
      run
    };

    if (this.size && this._queue[this.size - 1].priority >= opts.priority) {
      this._queue.push(element);
      return;
    }

    const index = lowerBound(this._queue, element, (a, b) => b.priority - a.priority);
    this._queue.splice(index, 0, element);
  }

  dequeue() {
    return this._queue.shift().run;
  }

  get size() {
    return this._queue.length;
  }
}

class PQueue {
  constructor(opts) {
    opts = Object.assign({
      concurrency: Infinity,
      queueClass: PriorityQueue
    }, opts);

    if (opts.concurrency < 1) {
      throw new TypeError('Expected `concurrency` to be a number from 1 and up');
    }

    this.queue = new opts.queueClass(); // eslint-disable-line new-cap
    this._queueClass = opts.queueClass;
    this._pendingCount = 0;
    this._concurrency = opts.concurrency;
    this._resolveEmpty = () => {};
    this._resolveIdle = () => {};
  }

  _next() {
    this._pendingCount--;

    if (this.queue.size > 0) {
      this.queue.dequeue()();
    } else {
      this._resolveEmpty();

      if (this._pendingCount === 0) {
        this._resolveIdle();
      }
    }
  }

  add(fn, opts) {
    return new Promise((resolve, reject) => {
      const run = () => {
        this._pendingCount++;

        fn().then(
          val => {
            resolve(val);
            this._next();
          },
          err => {
            reject(err);
            this._next();
          }
        );
      };

      if (this._pendingCount < this._concurrency) {
        run();
      } else {
        this.queue.enqueue(run, opts);
      }
    });
  }

  addAll(fns, opts) {
    return Promise.all(fns.map(fn => this.add(fn, opts)));
  }

  clear() {
    this.queue = new this._queueClass(); // eslint-disable-line new-cap
  }

  onEmpty() {
    // Instantly resolve if the queue is empty
    if (this.queue.size === 0) {
      return Promise.resolve();
    }

    return new Promise(resolve => {
      const existingResolve = this._resolveEmpty;
      this._resolveEmpty = () => {
        existingResolve();
        resolve();
      };
    });
  }

  onIdle() {
    // Instantly resolve if none pending
    if (this._pendingCount === 0) {
      return Promise.resolve();
    }

    return new Promise(resolve => {
      const existingResolve = this._resolveIdle;
      this._resolveIdle = () => {
        existingResolve();
        resolve();
      };
    });
  }

  get size() {
    return this.queue.size;
  }

  get pending() {
    return this._pendingCount;
  }
}


// TEST ####################


const promises = new PQueue({
  concurrency: 4
});

const makePromise = (key, time) => {
  let response = null;
  return new Promise(resolve => {
    setTimeout(() => {
      response = `Promise ${key} resolved`;
      console.log(response);
      resolve(response);
    }, time);
  });
}

promises.add(() => makePromise('p1', 5000));
promises.add(() => makePromise('p2', 1000));
promises.add(() => makePromise('p3', 3000));
promises.add(() => makePromise('p4', 6000));
promises.add(() => makePromise('p5', 2000));
promises.add(() => makePromise('p6', 1500));
promises.add(() => makePromise('p7', 5500));
promises.add(() => makePromise('p8', 7000));

promises.onIdle().then(() => {
  console.log('Promises queue empty.');
});

In these sort of cases, it is best to change in backend where you can process results for thousand inputs send them at one instead of calling from a thousand times. Another way is to use promise I think.

You can also look at this link if it is applicable for you. I think these things answer your question

发布者:admin,转转请注明出处:http://www.yc00.com/questions/1742146501a4391204.html

相关推荐

发表回复

评论列表(0条)

  • 暂无评论

联系我们

400-800-8888

在线咨询: QQ交谈

邮件:admin@example.com

工作时间:周一至周五,9:30-18:30,节假日休息

关注微信