2016-07-22 26 views
0

Ich habe eine electron/node.js App, mit der ich Probleme habe. Insbesondere muss ich dem Benutzer ein Feedback über den Fortschritt einer langen Schleife synchroner Spawns geben, indem ich ein Element im DOM aktualisiere. Was tatsächlich angezeigt wird, ändert sich jedoch nicht während der Schleife. Sobald die gesamte Schleife abgeschlossen ist, wird das Element mit dem letzten max Indexwert aktualisiert.DOM redraw wird von childProcess.spawnSync blockiert

function dosomething(index,callback) { 
    childProcess.spawnSync('app1',... //takes 1 second 
    childProcess.spawnSync('app2',... //takes 6 seconds, depends on app1 running 1st 
    console.log('working on: ' + index); 
    callback(index); 
} 

function dosomethingelse(index) { 
    $('#somediv').html(index); //update progress bar 
    console.log('displaying: ' + $('#somediv').html()); 
} 

for(var i=0; i<max; i++){ //max is usually 10-100, loop takes 1 to 10 minutes 
    dosomething(i,dosomethingelse); 
} 

Wenn ich den Fortschritt html auf die Konsole Dump, es mit dem Index als Rückruf erhöht hat:

CONSOLE: 
working on: 0 
displaying: 0 
working on: 1 
displaying: 1 
working on: 2 
displaying: 2 
... 

Auch habe ich versucht, das div zu zwingen, neu zu zeichnen, indem Sie den folgenden Code nicht ausgeführt wird navis:

Von dem, was ich gelesen habe, macht die Verwendung von spawnSync meine binäre App im Sperrmodus auf Nodejs ausgeführt. Dies ist direkt gegen den nicht blockierenden Kern des Knotens, aber in meiner Situation absolut notwendig, da mein Kommandozeilenaufruf 6 Sekunden lang läuft und fast 100% CPU verbraucht. Wenn ich stattdessen den standardmäßigen asynchronen Spawn verwende, lande ich mit 50x 100% gleichzeitig ablaufenden Prozessen, die nach ein paar Minuten zur selben Zeit beendet werden. Auch hier gibt es keine Fortschrittsrückmeldung für den Benutzer. Ich verstehe nicht, warum mein Rückruf nicht abgeschlossen wird. Wenn ich die Funktionen so umschalte, dass dosomethingelse zuerst mit einem dosomething Callback aufgerufen wird, bekomme ich immer noch keine DOM-Updates.

Zwei andere Dinge, die ich versucht habe: npm sleep:

dosomething(i); 
var sleep = require('sleep'); 
sleep.usleep(100); 
dosomethingelse(i); 

Und deasync:

var deasync = require('deasync'); 
deasync(dosomething(i)); 
dosomethingelse(i); 

gleiches Ergebnis. Auch, wenn ich meine lang laufende dosomething Funktion herausnehme und sie durch eine sleep.sleep(3) ersetze, bekomme ich das gleiche Ergebnis. Der Knoten wechselt einfach von einer blockierenden Aufgabe zur nächsten, ohne die Benutzeroberfläche zu aktualisieren.

+0

Ich habe darüber nachgedacht, einen Befehlszeilenspooler wie diesen zu verwenden: http://vicerveza.homeunix.net/~viric/soft/ts/, durch den alle meine schweren CPU-Aufgaben laufen und ihn dann jede Sekunde abfragen Aktualisiere meinen Fortschrittsbalken. Problem bei diesem Ansatz ist, dass es nicht plattformübergreifend ist (zumindest mit diesem Spooler). Ich muss mit dem Windows-Port kreativer werden. – UltrasoundJelly

+0

Tun Sie es einfach async und fügen Sie es in eine Warteschlange ein? – mash

+0

Der Versuch, das Chrome-DOM zu aktualisieren, das der Benutzer als Teil der Elektronen-App sieht. – UltrasoundJelly

Antwort

1

Sie scheinen sich nur über zu viele asynchrone Prozesse Sorgen zu machen, die gleichzeitig ausgeführt werden. Sie können jedoch steuern, wie viele gleichzeitig ausgeführt werden.

Zum Beispiel wie folgt:

function runTask(tasks, index, callback) { 
    if (index >= tasks.length) { 
    callback(); 
    } 
    tasks[index](() => runTask(tasks, index + 1, callback)); 
} 

function queue(tasks, callback) { 
    runTask(tasks, 0, callback); 
} 

damit eine einfache Möglichkeit haben würde Ihre async Warteschlange laicht up:

const spawn = require('child_process').spawn; 

function customSpawn(command, args) { 
    return callback => { 
    const child = spawn(command, args); 
    child.on('close', callback); 
    } 
} 

queue([customSpawn('app1', []), customSpawn('app2', [])], dosomethingelse); 

ich nie eine der oben genannten Code getestet und daher Ich kann nicht für die Richtigkeit garantieren.

Werfen Sie auch einen Blick auf Versprechen und Generatoren, wenn Sie alle diese Rückrufe loswerden wollen.

Zum Beispiel mit dem Versprechen es könnte wie folgt aussehen:

function queue(tasks) { 
    let index = 0; 
    const runTask = arg => { 
    if (index >= tasks.length) { 
     return Promise.resolve(arg); 
    } 
    return new Promise((resolve, reject) => { 
     tasks[index++](arg).then(arg => resolve(runTask(arg))).catch(reject); 
    }); 
    }; 
    return runTask(); 
} 

const spawn = require('child_process').spawn; 

function customSpawn(command, args) { 
    return() => new Promise((resolve, reject) => { 
    const child = spawn(command, args); 
    child.on('close', code => { 
     if (code === 0) { 
     resolve(); 
     } else { 
     reject(); 
     } 
    }); 
    }); 
} 

queue([customSpawn('app1', []), customSpawn('app2', [])]) 
    .then(dosomethingelse) 
    .catch(err => console.error(err)); 

Try it!

Ein Hinweis in Bezug auf die for-Schleife: Erstellen Sie die Warteschlange mit dem, was Sie zuerst mit Schließungen tun wollen wie in customSpawn demonstriert und dann übergeben Sie es an queue.

+0

Wow, ich bin nicht gewohnt, auf diese Weise zu kodieren, Knoten ist ein Mindf # $%. Es dauert ein bisschen, das in meinem Projekt zu implementieren und zu sehen, ob es bueno ist. – UltrasoundJelly

+0

Können Sie klären, was Sie meinen, indem Sie die Warteschlange erstellen? Meinst du das ganze Array von Funktionsaufrufen ('customSpawn (...)' und 'updateprogress (i)' usw.), die in einer bestimmten Reihenfolge laufen müssen und dann an 'queue' übergeben werden? zB: 'var myqueue = ['customSpawn (' app1 ', [])", "customSpawn (' app2 ', [])", "Fortschritt (i)"], ... etc', dann 'queue (myqueue) ... ' – UltrasoundJelly

+0

@UltrasoundJelly richtig, denn sonst würden sie alle zur gleichen Zeit wieder laufen. – mash