JavaScript, React - sending multiple simultaneous ajax calls

随声附和 提交于 2019-11-29 17:08:45

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.com/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.

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 complete, then fetch next portion of IDs

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.com/w/cpp/algorithm/lower_bound
// Used to compute insertion index to keep queue sorted after insertion
function lowerBound(array, value, comp) {
  let first = 0;
  let count = array.length;

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

    if (comp(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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!