2017-08-31 56 views
1

在我的React應用程序中,我有一個參數數組(例如某些ID),它應該用作ajax調用隊列的參數。問題是該數組可能包含超過1000個項目,如果我只是遞歸地使用forEach循環進行ajax調用,瀏覽器頁面最終會在每個請求解析之前停止響應。JavaScript,React - 發送多個併發的ajax調用

是否有技術/庫,它可以允許發送ajax請求,例如,一次5個請求異步,只有當這些請求完成後,繼續下一個5?

後續問題:

React - Controlling multiple Ajax Calls

React - Controlling AJAX calls made to the server

回答

2

,如果你沒有被ES版本的限制,並且可以使用ES6那麼你應該考慮異步伺機

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)

異步/伺機意思對於等待異步調用的確切場景。基本上在異步函數中,直到await被解決,執行被暫停。在開始使用它們之前,您需要了解許諾和生成器。

+0

不幸的是,這沒有奏效,等待沒有等到5個電話完成之後再發射組。 –

+0

你好,你可以試試這個片段嗎?我覺得一個不好的gotcha基本上只是在它不會等待內部函數(舊代碼中的映射函數)的主函數內部工作。這應該工作 –

+0

現在我想要完成每個承諾的服務器調用。 –

2

好吧,讓我們一些事情理清。在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

+0

感謝您的評論,我專門檢查是否有一些圖書館來幫助我。或者像superagent或Axios這樣的圖書館可以照顧這個? –

+0

@LokeshAgrawal然後它被稱爲庫,而不是「技術」 –

+0

這很乾淨,優雅,非常感謝:) –

1

在這樣的病例的下一部分,最好是在後端更改您可以處理結果千元的投入一次發送而不是千次呼叫。另一種方法是使用promise我想。

如果它適用於您,也可以查看this link。我認爲這些事情回答你的問題

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

+0

謝謝Juliobetta,但這似乎太多的代碼。這與shyam babu的代碼有什麼不同? –

+0

兩者都解決了你的問題... shyam babu的解決方案更直接,而PQueue給了你另一種可能性,例如定義請求的優先級和諸如'onIdle'和'onEmpty'之類的事件。 – juliobetta

+0

請參考https://stackoverflow.com/questions/46251862/react-controlling-multiple-ajax-calls,後續問題 –