JavaScript,React - 同时发送多个ajax调用

时间:2017-08-31 18:43:53

标签: javascript ajax reactjs redux axios

在我的React应用程序中,我有一个参数数组(例如一些ID),它们应该用作ajax调用队列的参数。问题是数组可能包含超过1000个项目,如果我只是使用forEach循环递归地进行ajax调用,浏览器页面最终会在每个请求被解析之前停止响应。

是否有技术/库,可以允许发送ajax请求,例如,每次异步发送5个请求,只有当这些请求完成后,继续下一个5?

跟进问题:

React - Controlling multiple Ajax Calls

React - Controlling AJAX calls made to the server

4 个答案:

答案 0 :(得分:3)

好的,让我们解决一些问题。在JavaScript中,AJAX请求本质上是异步的。您选择在实现中使它们同步一些。

您需要做的是拥有一些请求数组,您可以从中一次弹出结果X,等待它们返回,然后重复。

{{1}}

将同时执行5个请求,等待它们完成,然后获取ID的下一部分

答案 1 :(得分:2)

我在项目中遇到了同样的问题。您需要的是一个优先级队列,以便控制将同时执行多少请求。我正在使用this library。由于p-queue实现很容易理解并且不是那么大,我已经将代码粘贴在下面的代码段中,只是为了向您展示它在最新行中的工作原理。

// 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.');
});

答案 2 :(得分:2)

如果您不受es版本限制并且可以使用es6,那么您应该查看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用于等待异步调用的确切场景。基本上在异步函数内部,直到await被解决,执行被暂停。在开始使用它们之前,您需要了解承诺和生成器。

答案 3 :(得分:0)

在这种情况下,最好在后端更改,您可以处理数千个输入的结果,将其发送到一个而不是一千次调用。另一种方法是使用promise我认为。

如果适用于您,您还可以查看this link。我认为这些事情可以回答你的问题