2016-10-27 6 views
0

Ich habe ein Projekt, wo ich eine Funktion schreiben muss, um mehrere Dinge nacheinander zu berechnen und dann die Ergebnisse in eine SQL DB schreiben. Leider muss ich dies mehr als 40.000 Mal wiederholen. Ich benutze node.js und verspricht, dies zu erreichen, aber die Speicherauslastung geht auf fast 2 GB oft das Programm stirbt gerade nach 10.000 oder so Berechnungen. Ich entwickelte meine eigene einheimische proveyEach-Funktion, die ein Array-Elemente im Sequantial nimmt und es mit Versprechen kettet.Verkettung einer Menge (> 40.000) native Versprechungen zusammen frisst zu viel Speicher

Was mache ich hier falsch ?:

function promiseEach(array,promiseFn){ 
    return new Promise(function(resolve,reject){ 
     try { 
      var promiseArray = []; 
      var json = { array: array, counter: 0 } 
      for (var i = 0; i < array.length; i++) { 
       promiseArray.push(promiseFn) 
      } 
      promiseArray.reduce(function(preFn,curFn,index,pArray){ 
       return preFn 
       .then(function(z){ return json.array[json.counter++] }) 
       .then(curFn) 
      }, 
      Promise.resolve(json.array[json.counter])) 
      .then(resolve,reject) 

     }catch(err){ 
      console.log("promiseEach ERROR:"); 
      reject(err) 
     } 
    }) 
} 
+1

40.000 ist viel ist es nicht. Ich würde anfangen zu prüfen, wie Sie diese Zahl zuerst reduzieren könnten –

+0

Was ist, wenn Sie '--max-old-space-size' zu ​​etwas in der Nähe von 500MB angeben? – zerkms

+0

Vielleicht nicht das Problem, aber achten Sie auf die [explizite Versprechen Konstruktion Antipattern] (http://stackoverflow.com/questions/23803743/what-ist-the-explicit-promise-construction-antipattern-and-how-do- ich vermeide es). – JLRishe

Antwort

3

Zwei Linien in den entsandten Code

.then(function(z){ return json.array[json.counter++] }) 
    then(curFn) 

scheinen promseFn, um anzuzeigen, ist mit einem aufgerufen werden neuer Parameter, nachdem die Operation, die von einem vorherigen Aufruf ausgeführt wurde, abgeschlossen ist und der erfüllte Wert des vorherigen Aufrufs nicht in der Versprechungskette verwendet wird.

Ein Vorschlag (39.999 oder so) Zwischen gekettet Versprechen vor der jeweiligen Funktion Rückkehr zu vermeiden erstellen, ist Zwischen Versprechen von promiseFn ruft ein Versprechen ab Werk Funktion zurück verwenden, wenn sie erfüllt werden und eine endgültige Zusage zurück, die ist nur erfüllt durch das letzte Zwischenversprechen.

Das Konzept Code, der einen Aufruf an reduce enthält nicht folgt, da keine Verringerung Operation ausgeführt wird:

function promiseEach(array, promiseFn) { 
    var resolve, reject; 
    var counter = 0; 
    var final = new Promise(function(r, j) { resolve = r; reject = j}); 
    function nextPromise() { 
     promiseFn(array[counter++]) 
     .then((counter < array.length ? nextPromise : resolve), reject); 
    } 
    if(array.length) { 
     nextPromise(); 
    } 
    else { 
     reject(new Error("promiseEach called on empty array")); 
    } 
    return final; 
} 

Added Anmerkungen:

Die promiseEach Funktion oben in Knoten/js getestet mit sowohl eine synchrone als auch eine asynchrone promiseFn mit keine gleichzeitige Aktualisierungen der Daten-Array.

  • Synchronous-Test

    function promiseSynch(z) { 
        return new Promise(function(resolve,reject){resolve(z);}); 
    } 
    

konnte einen Array mit 1 Million (1000,000) Einträgen) in etwa 10 Sekunden verarbeiten, um einen Win7 i86 Notebook mit 1 GB Speicher und mit einem Browser , Editor und Aufgabenmanager öffnen gleichzeitig. Die Speicherbelegung war bei ungefähr 80% des verfügbaren Speichers flach.

  • Asynchronous-Test

    function promiseAsynch(z) { 
        var resolve; 
        function delayResolve() { 
         resolve(z); 
        } 
        return new Promise((r, j)=>{ 
         resolve = r; 
         setTimeout(delayResolve,1); 
        }); 
    } 
    

verwalten eine Reihe von 100.000 Einträgen in etwas mehr als 20 Minuten auf dem gleichen Notebook, wieder mit flacher Speicherauslastung von rund 80%.

Schlussfolgerung

Die Tests, die nicht einfach Speicherlecks verursachen Versprechungen schlagen, weil sie verwendet werden, und dass sie die Handhabung Passieren langwierige Arraydaten sequentiell in ein Versprechen Funktion zurückkehrt fähig sein.

Dies deutet darauf hin, dass es einige andere Ursachen für Speicherzuordnungsfehler oder mysteriöse Verarbeitungsausfälle gibt, die gefunden werden müssen, bevor das Problem vollständig gelöst werden kann. Als Vorschlag können Sie mit einer Suche nach Speicherleckproblemen in Verbindung mit der verwendeten DB-Bibliothek beginnen.

+0

Sind Sie sich bewusst, dass ein Limit für rekursive Aufrufe wie die vorgeschlagene ist? Ich habe tatsächlich mit einer Lösung begonnen, wie Sie sie vorgeschlagen haben, aber einen Fehler wie "zu viel Rekursion" bekommen, nachdem die Größe des Arrays auf ein bestimmtes Level gestiegen ist. Deshalb bin ich zum Array # reduce call gewechselt, welches dieses Problem nicht zu haben scheint. Ich werde versuchen, einige zusätzliche Tests durchzuführen. – ucipass

+0

Beim Aufruf von 'nextPromise' oben ist keine Rekursion beteiligt. Promise-Reaction-Handler werden asynchron aufgerufen, mit einem sauberen Stack, in einem frischen Thread, nachdem das Versprechen "then" aufgerufen wurde. – traktor53

+0

Mein Programm ist gerade fertig mit Ihrer Funktion und gerade als es um 39000 von 41000 Zähler kam, bekam ich eine Core Dump Nachricht mit: FATAL ERROR: CALL_AND_RETRY_LAST Zuweisung fehlgeschlagen - Prozess nicht genügend Speicher Abgebrochen (Core Dumped) werde ich Überprüfen Sie meine Datenzuordnung, während ich Attribute zu den JSON-Mitgliedern des oben genannten Arrays hinzufüge und lösche. – ucipass

6

Nun, du bist die Funktion für einen Start overcomplicating - von dem, was ich sagen kann, sind Sie promiseFn für jeden der Inhalt von Array aufrufen, in Folge

Der folgende Code ist in Funktion, um Ihren Code

function promiseEach(array, promiseFn) { 
    return array.reduce(function(prev, item) { 
     return prev.then(function() { 
      return promiseFn(item); 
     }); 
    }, Promise.resolve()); 
} 

Keine Garantie identisch, dass dies das eigentliche Problem beheben wird, obwohl

für Vollständigkeit, wie Sie in NodeJS codieren, den gleichen Code wie in ES2015 geschrieben +

let promiseEach = (array, promiseFn) => 
    array.reduce((prev, item) => 
     prev.then(() => 
      promiseFn(item) 
     ) 
    , Promise.resolve()); 
+0

Ihr Code ist in der Tat viel einfacher und ich schätze das Neuschreiben !!! Ich werde mein Programm ausführen, wenn es einen Unterschied macht, aber eine größere Frage ist, ob ich die gleiche Funktion millionenfach ausführen muss, würde die Verkettung von 1 Million Versprechungen der beste Weg sein, sequenzielle asynchrone Funktionen auszuführen? – ucipass

+1

Ich glaube nicht, dass dieser Code das Problem beheben wird. Ich denke, Traktor53 ist auf dem richtigen Weg, um ehrlich zu sein. –

Verwandte Themen