前段時間在用Promise.all執行一個非常大批量的操作時遇到一個奇怪的問題。 這個Promise.all需要遍歷一個指定目錄中的所有文件,並以非同步的方式讀取文件內容併進行後續操作。由於目錄中的文件數目比較多(大約8000+),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並串聯起來執行,從而規避上述瀏覽器中所出現的錯誤。