2015-10-26 43 views
6

我有一個在SharePoint 2013頁面上運行的Angular SPA。在代碼中,我使用$ q從10個不同的SharePoint列表中使用REST提取數據,然後將它們合併爲一個用於網格的JSON對象。該代碼運行並輸出預期的合併數據,但它有點漏洞並且在一段時間後崩潰瀏覽器。如何更有效地將REST調用結果合併到Angular應用程序中

下面是在服務代碼:

factory.getGridInfo = function() { 
    var deferred = $q.defer(); 

    var list_1a = CRUDFactory.getListItems("ListA", "column1,column2,column3"); 
    var list_1b = CRUDFactory.getListItems("ListB", "column1,column2,column3"); 
    var list_2a = CRUDFactory.getListItems("ListC", "column4"); 
    var list_2b = CRUDFactory.getListItems("ListD", "column4"); 
    var list_3a = CRUDFactory.getListItems("ListE", "column5"); 
    var list_3b = CRUDFactory.getListItems("ListF", "column5"); 
    var list_4a = CRUDFactory.getListItems("ListG", "column6"); 
    var list_4b = CRUDFactory.getListItems("ListH", "column6"); 
    var list_5a = CRUDFactory.getListItems("ListI", "column7"); 
    var list_5b = CRUDFactory.getListItems("ListJ", "column7"); 

    $q.all([list_1a, list_1b, list_2a, list_2b, list_3a, list_3b, list_4a, list_4b, list_5a, list_5b]) 
    .then(function(results){ 
     var results_1a = results[0].data.d.results; 
     var results_1b = results[1].data.d.results; 
     var results_2a = results[2].data.d.results; 
     var results_2b = results[3].data.d.results; 
     var results_3a = results[4].data.d.results; 
     var results_3b = results[5].data.d.results; 
     var results_4a = results[6].data.d.results; 
     var results_4b = results[7].data.d.results; 
     var results_5a = results[8].data.d.results; 
     var results_5b = results[9].data.d.results; 

     var combined_1 = results_1a.concat(results_1b); 
     var combined_2 = results_2a.concat(results_2b); 
     var combined_3 = results_3a.concat(results_3b); 
     var combined_4 = results_4a.concat(results_4b); 
     var combined_5 = results_5a.concat(results_5b); 

     for(var i = 0; i < combined_1.length; i++){ 
      var currObj = combined_1[i]; 
      currObj["column4"] = combined_2[i].column4; 
      currObj["column5"] = combined_3[i].column5; 
      currObj["column6"] = combined_4[i].column6; 
      currObj["column7"] = combined_5[i].column7; 

      factory.newObjectArray[i] = currObj; 

     } 
     deferred.resolve(factory.newObjectArray); 
    }, 
    function (error) { 
     deferred.reject(error); 
    });   
    return deferred.promise; 
}; 

下面是CRUDFactory的REST調用:

factory.getListItems = function (listName, columns){ 
    var webUrl = _spPageContextInfo.webAbsoluteUrl + "/_api/web/lists/getByTitle('"+listName+"')/items?$select="+columns+"&$top=5000"; 
    var options = { 
     headers: { "Accept": "application/json; odata=verbose" }, 
     method: 'GET', 
     url: webUrl     
    }; 
    return $http(options); 
}; 

然後這裏的控制器位:

$scope.refreshGridData = function(){ 
    $scope.hideLoadingGif = false; 
    $scope.GridData = ""; 
    GlobalFactory.getGridInfo() 
    .then(function(){ 
     $scope.GridData = GlobalFactory.newObjectArray; 
     $scope.hideLoadingGif = true; 
    }); 
}; 

更新1:每個請求,這是HTML(只是一個簡單的div,w e're使用角上的UI網)

<div ui-grid="GridOptions" class="grid" ui-grid-selection ui-grid-exporter ui-grid-save-state></div> 

此代碼首先聲明瞭一些得到調用,然後使用$ q.all遍歷調用和獲取數據。然後它存儲結果並將它們合併爲5個總數組。然後,因爲我的列表結構是正確的和靜態的,所以我可以遍歷其中一個合併數組,並將數據從其他數組抽取到一個主數組中,我將其分配給factory.newObjectArray,我聲明爲一個全球性的服務,並用作我的網格數據源。

該代碼運行並不踢任何錯誤,但問題是(我相信)「getGridInfo」函數。如果我沒有註釋掉任何REST調用,那麼瀏覽器會使用45 MB數據,這些數據不會被GC拾取,然後每複製一次,直到會話結束或崩潰。如果我只將一個電話註釋掉,我的頁面只能使用18.4 MB的內存,這很高,但我可以忍受。

那麼有什麼交易?我需要摧毀某個地方的東西嗎?如果是這樣,什麼和如何?或者這是否與我正在使用的REST函數有關?

更新2:網格正在使用的返回結果(factory.newObjectArray)包含總共5,450個項目,每個項目在合併之後具有大約80個屬性。上面的代碼被簡化了,並且顯示了每個列表中的幾個列的拉動,但實際上,我將每列拉動5-10列。

+0

我看不出任何根本上錯誤的代碼。你正在使用哪種瀏覽器? – link64

+0

IE11或FF 38.2(在此環境中無法訪問Chrome) – Josey

+0

這真是一個恥辱,因爲chrome具有可以向您顯示正在發生的事情的分析器。你可以顯示標記嗎?這個答案http://stackoverflow.com/a/25193339/1248716表明泄漏是由通過ng-repeat創建的DOM元素引起的,並且不會被釋放。 – link64

回答

1

在這一天你是結束處理大量數據,因此內存問題可能總是成爲問題,您應該考慮是否需要將所有數據存儲在內存中。

您應該嘗試實現的主要目標是限制數組的重複,儘量保持內存佔用儘可能低,並在完成處理時儘可能快地釋放內存。

請考慮以下內容。你提到實際返回的列數比你的例子多,所以我考慮到了這一點。

factory.getGridInfo = function() { 

    var deferred = $q.defer(), 

    // list definitions 
    lists = [ 
     { name: 'ListA', columns: ['column1', 'column2', 'column3'] }, 
     { name: 'ListB', columns: ['column1', 'column2', 'column3'], combineWith: 'ListA' }, 
     { name: 'ListC', columns: ['column4'] }, 
     { name: 'ListD', columns: ['column4'], combineWith: 'ListC' }, 
     { name: 'ListE', columns: ['column5'] }, 
     { name: 'ListF', columns: ['column5'], combineWith: 'ListE' }, 
     { name: 'ListG', columns: ['column6'] }, 
     { name: 'ListH', columns: ['column6'], combineWith: 'ListG' }, 
     { name: 'ListI', columns: ['column7'] }, 
     { name: 'ListJ', columns: ['column7'], combineWith: 'ListI' }, 
    ], 

    // Combines two arrays without creating a new array, mindful of lenth limitations 
    combineArrays = function (a, b) { 
     var len = b.length; 
     for (var i = 0; i < len; i = i + 5000) { 
     a.unshift.apply(a, b.slice(i, i + 5000)); 
     } 
    }; 

    $q.all(lists.map(function (list) { return CRUDFactory.getListItems(list.name, list.columns.join()); })) 
    .then(function (results) { 

    var listResultMap = {}, var baseList = 'ListA'; 

    // map our results to our list names 
    for(var i = 0; i < results.length; i++) { 
     listResultMap[lists[i].name] = results[i].data.d.results; 
    } 

    // loop around our lists 
    for(var i = 0; i < lists.length; i++) { 
     var listName = lists[i].name, combineWith = lists[i].combineWith; 
     if(combineWith) { 
     combineArrays(listResultMap[combineWith], listResultMap[listName]); 
     delete listResultMap[listName]; 
     } 
    } 

    // build result 
    factory.newObjectArray = listResultMap[baseList].map(function(item) { 
     for(var i = 0; i < lists.length; i++) { 
     if(list.name !== baseList) { 
      for(var c = 0; c < lists[i].columns.length; c++) { 
      var columnName = lists[i].columns[c]; 
      item[columnName] = listResultMap[list.name][columnName]; 
      } 
     } 
     } 
     return item; 
    }); 

    // clean up our remaining results 
    for (var i = 0; i < results.length; i++) { 
     delete results[i].data.d.results; 
     delete results[i]; 
    } 

    deferred.resolve(factory.newObjectArray); 

    }, 
    function (error) { 
    deferred.reject(error); 
    }); 
    return deferred.promise; 
}; 
+0

由於需要動態網格交互,用戶可以隱藏/顯示列並將各種數據位導出到Excel,所以我無法使數據源更小。至於你的代碼,你提出了一些我將要嘗試的好技術。我將在實施它們後發佈結果。 – Josey

+0

我使用了你的地圖技術,並且對你的unshift方法進行了一些調整,還有一些改動了結果生成器,我能夠將初始加載降至19.1 MB,從而有效地削減了8 MB以上。是GC工作更好,刷新只泄露1 - 1.2 MB,這可能是由於ui路由器或其他角元素。幹得不錯!積分獎勵。 – Josey

+0

賞金昨天過期...對不起肖恩。我標記你的答案,雖然和upvoted它的價值 – Josey

0

我會建議添加某種分頁選項......將所有結果添加到一個大列表可能不是一個好主意。

接下來,我會建議反對ng-repeat或向repeat函數添加「track by」。

退房:http://www.alexkras.com/11-tips-to-improve-angularjs-performance/

小提琴手您的疑問,該問題可能呈現DOM中的所有元素......這可能是有點慢(調查)

+0

分頁有點幫助(頁內存使用率從45 MB降低到40 MB)。當這些數據進入Angular-Ui-Grid時,Ng-repeat是一個非問題。至於Fiddler,我沒有權限在這個環境中使用它。我會繼續閱讀您發佈的鏈接,看看我是否無法揭示其他隱藏的細微差別。 – Josey

相關問題