2015-10-26 6 views
6

Mam Angular SPA działające na stronie SharePoint 2013. W kodzie używam $ q do pobierania danych z 10 różnych list programu SharePoint przy użyciu usługi REST, a następnie łączenia ich w jeden obiekt JSON w celu użycia w siatce. Kod działa i wyprowadza zamierzone scalone dane, ale jest nieszczelny i po chwili zawiesza przeglądarkę.Jak połączyć wyniki REST call w aplikacji Angular, bardziej wydajnie

Oto kod w serwisie:

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; 
}; 

Oto wezwanie REST w 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); 
}; 

I wtedy oto nieco kontroler:

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

UPDATE 1: Na życzenie, oto HTML (tylko prosty div, który w e're użyciu kątowej-UI-siatkę na)

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

Kod ten rozpoczyna się poprzez uznanie niektórych połączeń, a następnie używa $ q.all iteracyjne nad połączeniami i inne dane. Następnie przechowuje wyniki i łączy je w 5 tablic łącznie. Następnie, ponieważ struktura mojej listy jest poprawna i statyczna, jestem w stanie powtórzyć jeden z scalonych tablic i pobrać dane z innych tablic do jednej głównej tablicy, którą przypisuję do fabryki.newObjectArray, którą deklaruję jako globalny w mojej usłudze i jako moje źródło danych w sieci.

Kod działa i nie wykopie żadnych błędów, ale problem dotyczy (moim zdaniem) funkcji "getGridInfo". Jeśli nie skomentuję żadnego z wywołań REST, przeglądarka używa 45 MB danych, które nie zostaną odebrane przez GC, a następnie są dodawane do każdego kliknięcia, aż do zakończenia sesji lub awarii. Jeśli skomentuję wszystkie połączenia oprócz jednej, moja strona używa tylko 18,4 MB pamięci, co jest wysokie, ale mogę z tym żyć.

Więc jaka jest umowa? Czy muszę coś gdzieś zniszczyć? Jeśli tak, to co i jak? Czy to odnosi się do funkcji REST, której używam?

UPDATE 2: spowodować Zwrot że siatka jest za pomocą (The factory.newObjectArray) zawiera w sumie 5,450 elementów, a każdy element ma około 80 właściwości po połączeniu. Powyższy kod jest uproszczony i pokazuje ciągnięcie kilku kolumn na liście, ale w rzeczywistości generuję 5-10 kolumn na liście.

+0

Nie widzę nic zasadniczo nie tak z Twoim kodem. Z której przeglądarki korzystasz? – link64

+0

IE11 lub FF 38,2 (brak dostępu do Chrome w tym środowisku) – Josey

+0

To wstyd, ponieważ Chrome ma profilera, który może pokazać, co się dzieje. Czy możesz pokazać znaczniki? Ta odpowiedź http://stackoverflow.com/a/25193339/1248716 sugeruje, że wycieki są powodowane przez elementy DOM tworzone przez Ng-repeat i nie są one wypuszczane. – link64

Odpowiedz

1

Pod koniec dnia jesteś obsługa wielu danych, więc problemy z pamięcią zawsze będą stanowić problem i prawdopodobnie powinieneś rozważyć, czy potrzebujesz mieć wszystkie dane w pamięci.

Głównym celem, do którego prawdopodobnie powinieneś dążyć, jest ograniczenie powielania tablic i próba utrzymania możliwie najniższego poziomu pamięci oraz zwolnienie pamięci tak szybko, jak to możliwe, kiedy skończysz przetwarzanie.

Proszę wziąć pod uwagę następujące kwestie. Wspomniałeś, że rzeczywista liczba zwracanych kolumn jest większa niż Twój przykład, więc wziąłem to pod uwagę.

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

Ze względu na potrzebę dynamicznej interakcji z siecią, w której użytkownicy ukrywają/wyświetlają kolumny i eksportują różne kawałki danych do programu Excel, nie mogę spowodować, że źródło danych będzie mniejsze. Jeśli chodzi o twój kod, przedstawiłeś kilka dobrych technik, które spróbuję. Publikuję wyniki po ich wdrożeniu. – Josey

+0

Użyłem twojej techniki mapowania, a przy kilku poprawkach do metody Unshift i kilku innych do konstruktora wyników, udało mi się uzyskać początkowe obciążenie do 19,1 MB, skutecznie goląc 8+ MB. Prawdziwy kicker jednak jest to, że GC działa lepiej, a odświeża się tylko przecieka 1 - 1,2 MB, co można przypisać do interfejsu użytkownika lub do jakiegoś innego elementu kątowego. Dobra robota! Punkty przyznane. – Josey

+0

Bounty wygasł wczoraj ... Przepraszam Shaun. Oznaczyłem twoje jako odpowiedź i wznowiłem za to, co jest warte – Josey

0

Proponuję dodać opcję stronicowania ... Być może nie jest dobrym pomysłem dodawanie wszystkich wyników do jednej dużej listy.

Następnie sugerowałbym, aby powtórzyć powtórzenie lub dodać "ślad po" do funkcji powtarzania.

Check out: http://www.alexkras.com/11-tips-to-improve-angularjs-performance/

Skrzypek zapytań, problem jest prawdopodobnie renderowania wszystkie elementy w DOM ... co może być trochę powolna (badać)

+0

Paginacja pomaga odrobinę (przynosi zużycie pamięci do 40 MB z 45 MB). Powtarzanie Ng nie jest problemem, ponieważ dane przechodzą do Angular-Ui-Grid. Co do Fiddlera, nie mam uprawnień do korzystania z niego w tym środowisku. Przeczytam opublikowany link i zobaczę, czy nie mogę odkryć innego ukrytego niuansu. – Josey

Powiązane problemy