如何將一個大的Promise.all拆分爲幾個較小的部分依次執行

  前段時間在用Promise.all執行一個非常大批量的操作時遇到一個奇怪的問題。

  這個Promise.all需要遍歷一個指定目錄中的所有文件,並以異步的方式讀取文件內容並進行後續操作。由於目錄中的文件數目比較多(大約8000+),Promise.all在執行的過程中有許多文件讀取失敗,但是如果指定一個文件數量比較少的目錄則不存在這個問題。查看瀏覽器的Network會看到一開始的時候這些文件的訪問都是pending狀態,但是隨着數量的增加,一部分pending最後都變成failed了。然後在Console中也會看到有許多的Failed to load resource: net::ERR_INSUFFICIENT_RESOURCES錯誤。

  然後又在其它不同的瀏覽器中進行了嘗試,發現Chrome內核的瀏覽器(Chrome和Edge)基本都存在這個問題,但是FireFox工作正常。初步斷定有可能是瀏覽器自身內部機制導致的,所以嘗試看看能否將併發的數量減少以規避錯誤。

  下面是優化之前的代碼:

var aPromises = [];
paths.forEach(function(sPath) {
    aPromises.push(readDocument(sPath).then(function(oDocument) {
        // other process
        return oDocument;
    }));
});
return Q.all(aPromises);

  當遇到paths數組很大時,有較大的概率會出現Failed to load resource: net::ERR_INSUFFICIENT_RESOURCES錯誤。

  下面是優化之後的代碼:

 1 var fnPromise = function(docs) {
 2     var aPromises = [];
 3     docs.forEach(function(sPath) {
 4         aPromises.push(readDocument(sPath).then(function(oDocument) {
 5             // other process
 6             return oDocument;
 7         }));
 8     });
 9     return Q.all(aPromises);
10 }
11 
12 var _MAX_COCURRENCY = 50;
13 var aFiles = [];
14 var promise;
15 var index = 0;
16 while (index < paths.length) {
17     if (index + _MAX_COCURRENCY < paths.length) {
18         aFiles.push(paths.slice(index, index + _MAX_COCURRENCY));
19     } else {
20         aFiles.push(paths.slice(index));
21     }
22     index += _MAX_COCURRENCY;
23 }
24 aFiles.forEach(function(aSegFiles){
25     if (!promise) {
26         promise = fnPromise(aSegFiles);
27     } else {
28         promise = promise.then(function(results){
29             return fnPromise(aSegFiles).then(function(aResults){
30                 return results.concat(aResults);
31             });
32         })
33     }
34 });
35 return promise;

  上面的代碼中,while循環將一個較大的paths數組拆分成若干個較小的數組並存放到aFiles數組中。aFiles是一個二維數組,它的每一個元素是paths數組的一部分,其大小取決於_MAX_COCURRENCY常量的值。第24行對aFiles數組進行遍歷,如果promise沒有被初始化,則將函數fnPromise賦值給它。否則,通過第28行將fnPromise函數執行後的then方法重新賦值給變量promise,這一行是將多個Promise串聯起來的關鍵。函數fnPromise只對較小的數組進行Promise.all操作。

  通過上面的代碼,可以成功將一個大的Promise.all拆分爲若干個較小的Promise並串聯起來執行,從而規避上述瀏覽器中所出現的錯誤。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章