2015-10-26 29 views
6

Possiedo un ANGular SPA in esecuzione su una pagina SharePoint 2013. Nel codice, sto usando $ q per estrarre dati da 10 diversi elenchi di SharePoint utilizzando REST e quindi unendoli in un unico oggetto JSON da utilizzare in una griglia. Il codice esegue e restituisce i dati incorporati, ma perde tempo e arresta il browser dopo un po '.Come unire i risultati delle chiamate REST in un'app Angolare in modo più efficiente

Ecco il codice nel servizio:

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

Ecco la chiamata REST in 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); 
}; 

E allora ecco il bit di controllo:

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

UPDATE 1: Per richiesta, ecco l'HTML (solo un semplice div che w iamo utilizzando angolare-ui-griglia)

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

Questo codice inizia dichiarando alcuni ottenere chiamate e poi usa $ q.all per scorrere i chiamate e ottenere i dati. Quindi memorizza i risultati e li unisce a 5 array totali. Quindi, dato che la mia struttura delle liste è corretta e statica, sono in grado di eseguire iterazioni su uno degli array uniti e di trasferire i dati dagli altri array in un array master che sto assegnando a factory.newObjectArray, che dichiaro come un servizio globale nel mio servizio e utilizzo come origine dati della mia griglia.

Il codice viene eseguito e non genera alcun errore, ma il problema riguarda (credo) la funzione "getGridInfo". Se non commento nessuna delle chiamate REST, il browser utilizza 45 MB di dati che non vengono rilevati da GC, che viene poi composto per ogni clic fino al termine della sessione o in caso di arresto anomalo. Se commento tutte le chiamate tranne una, la mia pagina utilizza solo 18,4 MB di memoria, che è alta ma posso conviverci.

Allora, qual è l'affare? Devo distruggere qualcosa da qualche parte? Se sì, cosa e come? O questo si riferisce alla funzione REST che sto usando?

UPDATE 2: Il risultato di ritorno che la griglia utilizza (il factory.newObjectArray) contiene un totale di 5.450 articoli e ogni articolo ha circa 80 proprietà dopo l'unione. Il codice sopra è semplificato e mostra l'estrazione di un paio di colonne per elenco, ma in realtà sto tirando 5-10 colonne per elenco.

+0

Non riesco a vedere nulla di fondamentalmente sbagliato nel codice. Quale browser stai usando? – link64

+0

IE11 o FF 38.2 (nessun accesso a Chrome in questo ambiente) – Josey

+0

Questo è un peccato, dato che Chrome ha il profiler che può mostrarti cosa sta succedendo. Puoi mostrare il markup? Questa risposta http://stackoverflow.com/a/25193339/1248716 suggerisce che le perdite sono causate da elementi DOM che vengono creati tramite ng-repeat e che non vengono rilasciati – link64

risposta

1

Alla fine della giornata si è occuparsi di molti dati, quindi i problemi di memoria potrebbero essere sempre un problema e probabilmente dovresti considerare se hai bisogno di avere tutti i dati in memoria.

L'obiettivo principale che dovresti probabilmente cercare di ottenere è limitare la duplicazione degli array e cercare di mantenere il footprint della memoria il più basso possibile e liberare la memoria il più velocemente possibile quando hai finito l'elaborazione.

Si prega di considerare quanto segue. Hai menzionato il numero effettivo di colonne restituite più del tuo esempio, quindi l'ho preso in considerazione.

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

A causa della necessità di un'interazione dinamica della griglia in cui gli utenti nascondono/mostrano colonne ed esportano vari bit di dati in Excel, non riesco a rendere l'origine dei dati più piccola. Per quanto riguarda il tuo codice, hai tirato fuori un paio di buone tecniche che ho intenzione di provare. Pubblicherò i risultati dopo averli implementati. – Josey

+0

Ho usato la tua tecnica della mappa e, con alcune modifiche al metodo di nonshift, e un po 'di più al builder dei risultati, sono riuscito a ridurre il carico iniziale a 19.1 MB, raschiando efficacemente 8+ MB. Il vero kicker è che GC sta funzionando meglio e gli aggiornamenti stanno perdendo solo 1 - 1.2 MB, che potrebbero essere attribuiti a ui-router o qualche altro elemento angolare. Bel lavoro! Punti assegnati. – Josey

+0

La Bounty è scaduta ieri ... Mi dispiace Shaun. Ho contrassegnato il tuo come risposta e in aumento per quello che vale – Josey

0

Vorrei suggerire di aggiungere una sorta di opzione di paging ... Forse non è una buona idea aggiungere tutti i risultati a un'unica grande lista.

Avanti vorrei suggerire contro ng-repeat o aggiungere una "traccia per" alla funzione di ripetizione.

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

Fiddler le vostre domande, il problema è probabilmente il rendering tutti gli elementi della dom ... che potrebbe essere un po 'lento (indagare)

+0

L'impaginazione aiuta un pochino (porta l'utilizzo della memoria della pagina a 40 MB da 45 MB). Ng-repeat è un non-problema in quanto questi dati vanno in una Angular-Ui-Grid. Per quanto riguarda Fiddler, non ho il permesso di usarlo in questo ambiente. Continuerò a leggere attraverso il link che hai postato e vedere se non riesco a scoprire qualche altra sfumatura nascosta. – Josey