【问题标题】:JavaScript, React - sending multiple simultaneous ajax callsJavaScript,React - 发送多个同时 ajax 调用
【发布时间】:2017-08-31 18:43:52
【问题描述】:

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

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

跟进问题:

React - Controlling multiple Ajax Calls

React - Controlling AJAX calls made to the server

【问题讨论】:

    标签: javascript ajax reactjs redux axios


    【解决方案1】:

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

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

    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)
    }
    

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

    【讨论】:

    • 感谢您的评论,我专门检查是否有一些图书馆可以帮助我解决这个问题。或者像 superagent 或 Axios 这样的库可以解决这个问题?
    • @LokeshAgrawal 然后它被称为库,而不是“技术”
    • Promise.all 将等待所有 5 个 Promise 在循环进入下一次迭代之前得到解决,对吗?
    • @LokeshAgrawal 正确:)
    • 请在stackoverflow.com/questions/46251862/… 上发表意见,这是一个后续问题。
    【解决方案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 用于等待异步调用的确切场景。基本上在 async 函数内部,直到等待解决,执行被暂停。在开始使用 Promise 和生成器之前,您需要了解它们。

    【讨论】:

    • 不幸的是,这不起作用,await 没有等待 5 次调用完成,然后再触发另一组。
    • 嗨,你能试试这个 sn-p 吗?我觉得一个不好的陷阱基本上 await 只能在 main 函数内部工作,它不会 await 内部函数(旧代码中的 map 函数)。这应该工作
    • 现在我想在每个承诺完成后进行一次服务器调用。
    • 在 fetch.then(()=>makeservercall) 内部,但是如果我这样做,那么整个 await 事情就不起作用了。
    • 这个 makeserverCall 是否需要 fetch 中的任何数据,还是只需要在 fetch 之后触发?它应该可以正常工作。
    【解决方案3】:

    我在一个项目中遇到了同样的问题。您需要的是一个优先级队列,以控制同时执行多少个请求。我正在使用this library。由于 p-queue 的实现简单易懂且规模不大,因此我将代码粘贴到下面的 sn-p 中,只是为了向您展示它在最新行中的工作原理。

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

    【讨论】:

    • 感谢 Juliobetta 但这似乎是一个太多的代码。这与 shyam babu 的代码有何不同?
    • 两者都能解决你的问题... shyam babu 的解决方案更直接,而 PQueue 为您提供了另一种可能性,比如定义请求和事件的优先级,例如 onIdleonEmpty
    • 请在stackoverflow.com/questions/46251862/…上发表意见,后续问题
    【解决方案4】:

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

    您也可以查看this link,如果它适用于您。我认为这些东西回答了你的问题

    【讨论】:

      猜你喜欢
      • 2017-01-27
      • 2015-07-07
      • 1970-01-01
      • 1970-01-01
      • 2013-03-23
      • 2012-06-12
      • 1970-01-01
      • 1970-01-01
      • 2018-02-25
      相关资源
      最近更新 更多