Как сливать остальные результаты звонка в угловой приложения более эффективно
У меня есть угловой спа-центр, работающий на странице SharePoint 2013. В коде я использую $q для извлечения данных из 10 различных списков SharePoint с помощью REST,а затем объединяю их в один объект JSON для использования в сетке. Код запускается и выводит предполагаемые объединенные данные, но он протекает и через некоторое время завершает работу браузера.
Вот код в сервисе:
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:
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, который мы используем angular-ui-grid)
<div ui-grid="GridOptions" class="grid" ui-grid-selection ui-grid-exporter ui-grid-save-state></div>
Этот код начинается с объявления некоторых вызовов get, а затем использует $q. all для перебора вызовов и получения данных. Затем он сохраняет результаты и объединяет их в 5 полных массивов. Затем, поскольку моя структура списка является правильной и статичной, я могу перебирать один из Объединенных массивов и извлекать данные из других массивов в один главный массив, который я использую. назначение на завод.newObjectArray, который я объявляю глобальным в своем сервисе и использую в качестве источника данных сетки.
Код выполняется и не вызывает никаких ошибок, но проблема (я полагаю) с функцией" getGridInfo". Если я не комментирую ни один из остальных вызовов, браузер использует 45 МБ данных, которые не улавливаются GC, которые затем усугубляются для каждого клика, пока сеанс не закончится или не произойдет сбой. Если я закомментирую все звонки, кроме одного, моя страница использует только 18,4 МБ память, которая высока, но я могу жить с ней.
Так в чем же дело? Мне нужно что-то где-то уничтожить? Если да, то что и как? Или это связано с функцией REST, которую я использую?
Обновление 2: возвращаемый результат, который использует сетка (фабрика.newObjectArray) содержит в общей сложности 5450 элементов, и каждый элемент имеет около 80 свойств после слияния. Приведенный выше код упрощен и показывает вытягивание пары столбцов в списке, но на самом деле я вытягивание 5-10 столбцов в списке.
2 ответа:
В конце дня вы имеете дело с большим количеством данных, поэтому проблемы с памятью потенциально всегда будут проблемой, и вы, вероятно, должны рассмотреть, нужно ли вам иметь все данные в памяти.
Основная цель, которую вы, вероятно, должны пытаться достичь, - это ограничить дублирование массивов и попытаться сохранить объем памяти как можно меньше, а также освободить память как можно быстрее, когда вы закончите обработку.
Пожалуйста, подумайте о следующем. Вы упомянули о том, что фактическое количество возвращаемых столбцов больше, чем в вашем примере, поэтому я принял это во внимание.
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; };
Я бы предложил добавить какой-то вариант подкачки... Возможно, это не очень хорошая идея, чтобы добавить все результаты в один большой список.
Далее я бы предложил против ng-repeat или добавить "track by" к функции repeat.
Проверьте: http://www.alexkras.com/11-tips-to-improve-angularjs-performance/
Fiddler ваши запросы, проблема, вероятно, рендеринга всех элементов в dom... Что может быть довольно медленно (исследовать)