Wie werden asynchrone Funktionsaufrufe in eine Synchronisierungsfunktion in Node.js oder Javascript eingeschlossen?

122

Angenommen, Sie verwalten eine Bibliothek, die eine Funktion verfügbar macht getData. Ihre Benutzer rufen es auf, um aktuelle Daten abzurufen:
var output = getData();
Unter der Haube werden Daten in einer Datei gespeichert, sodass Sie sie getDatamit Node.js integriert implementieren fs.readFileSync. Es ist beides offensichtlich getDataund fs.readFileSynces handelt sich um Synchronisierungsfunktionen. Eines Tages wurde Ihnen gesagt, dass Sie die zugrunde liegende Datenquelle auf ein Repo wie MongoDB umstellen sollen, auf das nur asynchron zugegriffen werden kann. Sie wurden auch angewiesen, Ihre Benutzer getDatanicht zu verärgern. Die API kann nicht geändert werden, um lediglich ein Versprechen zurückzugeben oder einen Rückrufparameter zu fordern. Wie erfüllen Sie beide Anforderungen?

Asynchrone Funktion mit Rückruf / Versprechen ist die DNA von JavasSript und Node.js. Jede nicht triviale JS-App ist wahrscheinlich von diesem Codierungsstil durchdrungen. Diese Praxis kann jedoch leicht zu einer sogenannten Rückrufpyramide des Untergangs führen. Schlimmer noch, wenn ein Code in einem Aufrufer in der Aufrufkette vom Ergebnis der asynchronen Funktion abhängt, muss dieser Code ebenfalls in die Rückruffunktion eingeschlossen werden, wodurch dem Anrufer eine Einschränkung des Codierungsstils auferlegt wird. Von Zeit zu Zeit muss eine asynchrone Funktion (die häufig in einer Bibliothek eines Drittanbieters bereitgestellt wird) in eine Synchronisierungsfunktion eingekapselt werden, um ein massives globales Re-Factoring zu vermeiden. Die Suche nach einer Lösung zu diesem Thema endete normalerweise mit Node Fibersoder davon abgeleitete npm-Pakete. Aber Fasern können das Problem, mit dem ich konfrontiert bin, einfach nicht lösen. Sogar das Beispiel des Autors von Fibers illustrierte den Mangel:

...
Fiber(function() {
    console.log('wait... ' + new Date);
    sleep(1000);
    console.log('ok... ' + new Date);
}).run();
console.log('back in main');

Tatsächliche Ausgabe:

wait... Fri Jan 21 2011 22:42:04 GMT+0900 (JST)
back in main
ok... Fri Jan 21 2011 22:42:05 GMT+0900 (JST)

Wenn die Funktion Fibre den asynchronen Funktionsschlaf wirklich synchronisiert, sollte die Ausgabe wie folgt lauten:

wait... Fri Jan 21 2011 22:42:04 GMT+0900 (JST)
ok... Fri Jan 21 2011 22:42:05 GMT+0900 (JST)
back in main

Ich habe ein weiteres einfaches Beispiel in JSFiddle erstellt und nach Code gesucht, um die erwartete Ausgabe zu erzielen. Ich akzeptiere eine Lösung, die nur in Node.js funktioniert, sodass Sie jedes npm-Paket benötigen können, obwohl Sie nicht in JSFiddle arbeiten.

abbr
quelle
2
Asynchrone Funktionen können in Node niemals synchronisiert werden, und selbst wenn dies möglich wäre, sollten Sie dies nicht tun. Das Problem ist, dass Sie im fs-Modul völlig getrennte Funktionen für den synchronen und asynchronen Zugriff auf das Dateisystem sehen können. Das Beste, was Sie tun können, ist, das Erscheinungsbild von Async mit Versprechungen oder Coroutinen (Generatoren in ES6) zu maskieren. Geben Sie zum Verwalten von Rückrufpyramiden Namen an, anstatt sie in einem Funktionsaufruf zu definieren, und verwenden Sie so etwas wie die asynchrone Bibliothek.
Qubyte
8
Für Dandavis sprudelt Async Implementierungsdetails in die Aufrufkette und erzwingt manchmal globales Refactoring. Dies ist nachteilig und sogar katastrophal für eine komplexe Anwendung, bei der Modularisierung und Eindämmung wichtig sind.
abbr
4
"Rückrufpyramide des Untergangs" ist nur die Darstellung des Problems. Promise kann es verbergen oder verschleiern, aber nicht die wahre Herausforderung angehen: Wenn der Aufrufer einer asynchronen Funktion von den Ergebnissen der asynchronen Funktion abhängt, muss er einen Rückruf verwenden, ebenso wie sein Aufrufer usw. Dies ist ein klassisches Beispiel für das Auferlegen von Einschränkungen Anrufer einfach wegen Implementierungsdetails.
abbr
1
@abbr: Danke für das Deasync-Modul, die Beschreibung Ihres Problems ist genau das, wonach ich gesucht habe und konnte keine praktikablen Lösungen finden. Ich habe mit Generatoren und Iterables herumgespielt, bin aber zu den gleichen Schlussfolgerungen gekommen wie Sie.
Kevin Jhangiani
2
Es ist erwähnenswert, dass es fast nie eine gute Idee ist , eine asynchrone Funktion zur Synchronisierung zu zwingen. Sie haben fast immer eine bessere Lösung, die die Asynchronität der Funktion intakt hält und gleichzeitig den gleichen Effekt erzielt (wie Sequenzierung, Variableneinstellung usw.).
Madaras Geist

Antworten:

104

deasync verwandelt die asynchrone Funktion in eine Synchronisierung, die mit einem Blockierungsmechanismus implementiert wird, indem die Ereignisschleife Node.js auf JavaScript-Ebene aufgerufen wird. Infolgedessen blockiert Deasync nur die Ausführung von nachfolgendem Code, ohne den gesamten Thread zu blockieren oder zu warten. Mit diesem Modul finden Sie hier die Antwort auf die jsFiddle-Herausforderung:

function AnticipatedSyncFunction(){
  var ret;
  setTimeout(function(){
      ret = "hello";
  },3000);
  while(ret === undefined) {
    require('deasync').runLoopOnce();
  }
  return ret;    
}


var output = AnticipatedSyncFunction();
//expected: output=hello (after waiting for 3 sec)
console.log("output="+output);
//actual: output=hello (after waiting for 3 sec)

(Haftungsausschluss: Ich bin Mitautor von deasync. Das Modul wurde nach dem Posten dieser Frage erstellt und hat keinen praktikablen Vorschlag gefunden.)

abbr
quelle
Hat sonst noch jemand Glück damit gehabt? Ich kann es nicht zum Laufen bringen.
Newman
3
Ich kann es nicht richtig machen. Sie sollten Ihre Dokumentation für dieses Modul verbessern, wenn Sie möchten, dass es mehr verwendet wird. Ich bezweifle, dass die Autoren genau wissen, welche Konsequenzen die Verwendung des Moduls hat, und wenn doch, dokumentieren sie sie mit Sicherheit nicht.
Alexander Mills
5
Bisher ist ein bestätigtes Problem im Github Issue Tracker dokumentiert. Das Problem wurde in Node v0.12 behoben. Der Rest, den ich kenne, sind lediglich unbegründete Spekulationen, die es nicht wert sind, dokumentiert zu werden. Wenn Sie glauben, dass Ihr Problem durch Deasync verursacht wird, veröffentlichen Sie ein in sich geschlossenes, duplizierbares Szenario, und ich werde es untersuchen.
Abb.
Ich habe versucht, es zu verwenden, und ich habe einige Verbesserungen in meinem Skript erhalten, aber ich hatte immer noch kein Glück mit dem Datum. Ich habe den Code wie folgt geändert: function AnticipatedSyncFunction(){ var ret; setTimeout(function(){ var startdate = new Date() //console.log(startdate) ret = "hello" + startdate; },3000); while(ret === undefined) { require('deasync').runLoopOnce(); } return ret; } var output = AnticipatedSyncFunction(); var startdate = new Date() console.log(startdate) console.log("output="+output); und ich erwarte 3 Sekunden Unterschied in der Datumsausgabe!
Alex
@abbr kann dies browserisiert und ohne Knotenabhängigkeit verwendet werden>
Gandhi
5

Es gibt auch ein npm-Synchronisationsmodul. Hiermit wird der Prozess der Ausführung der Abfrage synchronisiert.

Wenn Sie parallele Abfragen synchron ausführen möchten, beschränken Sie sich darauf, da der Knoten niemals auf eine Antwort wartet. Das Synchronisationsmodul eignet sich perfekt für diese Art von Lösung.

Beispielcode

/*require sync module*/
var Sync = require('sync');
    app.get('/',function(req,res,next){
      story.find().exec(function(err,data){
        var sync_function_data = find_user.sync(null, {name: "sanjeev"});
          res.send({story:data,user:sync_function_data});
        });
    });


    /*****sync function defined here *******/
    function find_user(req_json, callback) {
        process.nextTick(function () {

            users.find(req_json,function (err,data)
            {
                if (!err) {
                    callback(null, data);
                } else {
                    callback(null, err);
                }
            });
        });
    }

Referenzlink: https://www.npmjs.com/package/sync

Sanjeev Kumar
quelle
4

Wenn die Funktion Fibre den asynchronen Funktionsschlaf wirklich synchronisiert

Ja. Innerhalb der Faser wartet die Funktion vor der Protokollierung ok. Glasfasern machen asynchrone Funktionen nicht synchron, ermöglichen jedoch das Schreiben von synchron aussehendem Code, der asynchrone Funktionen verwendet und dann innerhalb von a asynchron ausgeführt wird Fiber.

Von Zeit zu Zeit muss eine asynchrone Funktion in eine Synchronisierungsfunktion eingekapselt werden, um ein massives globales Re-Factoring zu vermeiden.

Du kannst nicht. Es ist unmöglich, asynchronen Code synchron zu machen. Sie müssen dies in Ihrem globalen Code antizipieren und von Anfang an asynchron schreiben. Ob Sie den globalen Code in eine Glasfaser einwickeln, Versprechen, Versprechengeneratoren oder einfache Rückrufe verwenden, hängt von Ihren Vorlieben ab.

Mein Ziel ist es, die Auswirkungen auf den Anrufer zu minimieren, wenn die Datenerfassungsmethode von synchron auf asynchron geändert wird

Das können sowohl Versprechen als auch Fasern.

Bergi
quelle
1
Dies ist das ABSOLUT Schlimmste, was Sie mit Node.js tun können: "Synchron aussehender Code, der asynchrone Funktionen verwendet und dann asynchron ausgeführt wird." Wenn Ihre API dies tut, werden Sie Leben ruinieren. Wenn es asynchron ist, sollte es einen Rückruf erfordern und einen Fehler auslösen, wenn kein Rückruf bereitgestellt wird. Dies ist der beste Weg, um eine API zu erstellen, es sei denn, Ihr Ziel ist es, Menschen auszutricksen.
Alexander Mills
@AlexMills: Ja, das wäre in der Tat schrecklich . Glücklicherweise kann eine API dies jedoch nicht. Eine asynchrone API muss immer einen Rückruf akzeptieren / ein Versprechen zurückgeben / erwarten, dass sie innerhalb einer Glasfaser ausgeführt wird - ohne funktioniert sie nicht. Afaik, Fasern wurden hauptsächlich in Quick'n'Dirty-Skripten verwendet, die blockierten und keine Parallelität aufwiesen, aber asynchrone APIs verwenden möchten. Genau wie im Knoten gibt es manchmal Fälle, in denen Sie die synchronen fsMethoden verwenden würden.
Bergi
2
Ich mag im Allgemeinen Knoten. Vor allem, wenn ich Typoskript anstelle von reinem js verwenden kann. Aber dieser ganze asynchrone Unsinn, der alles durchdringt, was Sie tun, und buchstäblich jede Funktion in der Anrufkette infiziert, sobald Sie sich entscheiden, einen einzelnen asynchronen Anruf zu tätigen, ist etwas, das ich wirklich ... wirklich hasse. Async api ist wie eine Infektionskrankheit. Ein Aufruf infiziert Ihre gesamte Codebasis und zwingt Sie, den gesamten Code, den Sie haben, neu zu schreiben. Ich verstehe wirklich nicht, wie jemand argumentieren kann, dass dies eine gute Sache ist.
Kris
@Kris Node verwendet ein asynchrones Modell für E / A-Aufgaben, da es schnell und einfach ist. Sie können viele Dinge auch synchron erledigen, aber das Blockieren ist langsam, da Sie nichts gleichzeitig tun können - es sei denn, Sie entscheiden sich für Threads, die alles kompliziert machen.
Bergi
@Bergi Ich habe das Manifest gelesen, damit ich die Argumente kenne. Es ist jedoch nicht einfach, Ihren vorhandenen Code in den Moment zu ändern, in dem Sie den ersten API-Aufruf ohne Synchronisierungsäquivalent ausführen . Alles bricht und jede einzelne Codezeile muss überprüft werden. Es sei denn, Ihr Code ist trivial, ich garantiere ... es wird eine Weile dauern, bis er konvertiert ist und wieder funktioniert, nachdem das Ganze in eine asynchrone Sprache konvertiert wurde.
Kris
2

Sie müssen Versprechen verwenden:

const asyncOperation = () => {
    return new Promise((resolve, reject) => {
        setTimeout(()=>{resolve("hi")}, 3000)
    })
}

const asyncFunction = async () => {
    return await asyncOperation();
}

const topDog = () => {
    asyncFunction().then((res) => {
        console.log(res);
    });
}

Ich mag Pfeilfunktionsdefinitionen mehr. Jede Zeichenfolge der Form "() => {...}" kann aber auch als "function () {...}" geschrieben werden.

TopDog ist also nicht asynchron, obwohl eine asynchrone Funktion aufgerufen wird.

Geben Sie hier die Bildbeschreibung ein

BEARBEITEN: Mir ist klar, dass Sie häufig eine asynchrone Funktion in eine Synchronisierungsfunktion einbinden müssen. Für diese Situationen ist hier ein Partytrick:

const getDemSweetDataz = (req, res) => {
    (async () => {
        try{
            res.status(200).json(
                await asyncOperation()
            );
        }
        catch(e){
            res.status(500).json(serviceResponse); //or whatever
        }
    })() //So we defined and immediately called this async function.
}

Wenn Sie dies mit Rückrufen verwenden, können Sie einen Wrap durchführen, der keine Versprechen verwendet:

const asyncOperation = () => {
    return new Promise((resolve, reject) => {
        setTimeout(()=>{resolve("hi")}, 3000)
    })
}

const asyncFunction = async (callback) => {
    let res = await asyncOperation();
    callback(res);
}

const topDog = () => {
    let callback = (res) => {
        console.log(res);
    };

    (async () => {
        await asyncFunction(callback)
    })()
}

Wenn Sie diesen Trick auf einen EventEmitter anwenden, können Sie dieselben Ergebnisse erzielen. Definieren Sie den Listener des EventEmitter, in dem ich den Rückruf definiert habe, und geben Sie das Ereignis aus, in dem ich den Rückruf aufgerufen habe.

user2485309
quelle
1

Ich kann kein Szenario finden, das nicht mit Knotenfasern gelöst werden kann. Das Beispiel, das Sie mit Node-Fibers bereitgestellt haben, verhält sich wie erwartet. Der Schlüssel besteht darin, den gesamten relevanten Code in einer Faser auszuführen, damit Sie keine neue Faser an zufälligen Positionen starten müssen.

Sehen wir uns ein Beispiel an: Angenommen, Sie verwenden ein Framework, das der Einstiegspunkt Ihrer Anwendung ist (Sie können dieses Framework nicht ändern). Dieses Framework lädt die nodejs-Module als Plugins und ruft einige Methoden für die Plugins auf. Nehmen wir an, dieses Framework akzeptiert nur synchrone Funktionen und verwendet keine Fasern für sich.

Es gibt eine Bibliothek, die Sie in einem Ihrer Plugins verwenden möchten, aber diese Bibliothek ist asynchron und Sie möchten sie auch nicht ändern.

Der Haupt-Thread kann nicht ausgegeben werden, wenn keine Faser läuft, aber Sie können trotzdem Plugins mit Fasern erstellen! Erstellen Sie einfach einen Wrapper-Eintrag, der das gesamte Framework innerhalb einer Glasfaser startet, damit Sie die Ausführung über die Plugins erzielen können.

Nachteil: Wenn das Framework intern verwendet setTimeoutoder verwendet Promise, entgeht es dem Glasfaserkontext. Dies kann durch spöttisch umgingen werden setTimeout, Promise.thenund alle Event - Handler.

Auf diese Weise können Sie eine Faser erhalten, bis a Promiseaufgelöst ist. Dieser Code übernimmt eine asynchrone Funktion (Promise Returning) und setzt die Glasfaser fort, wenn das Versprechen aufgelöst wird:

Framework-entry.js

console.log(require("./my-plugin").run());

async-lib.js

exports.getValueAsync = () => {
  return new Promise(resolve => {
    setTimeout(() => {
      resolve("Async Value");
    }, 100);
  });
};

my-plugin.js

const Fiber = require("fibers");

function fiberWaitFor(promiseOrValue) {
  var fiber = Fiber.current, error, value;
  Promise.resolve(promiseOrValue).then(v => {
    error = false;
    value = v;
    fiber.run();
  }, e => {
    error = true;
    value = e;
    fiber.run();
  });
  Fiber.yield();
  if (error) {
    throw value;
  } else {
    return value;
  }
}

const asyncLib = require("./async-lib");

exports.run = () => {
  return fiberWaitFor(asyncLib.getValueAsync());
};

my-entry.js

require("fibers")(() => {
  require("./framework-entry");
}).run();

Wenn Sie ausführen node framework-entry.js, wird ein Fehler ausgegeben : Error: yield() called with no fiber running. Wenn Sie es ausführen node my-entry.js, funktioniert es wie erwartet.

Tamas Hegedus
quelle
0

Die Synchronisierung des Node.js-Codes ist in einigen Aspekten wie der Datenbank von entscheidender Bedeutung. Der eigentliche Vorteil von Node.js liegt jedoch im asynchronen Code. Da es sich um einen einzelnen Thread handelt, der nicht blockiert.

Wir können es mit wichtigen Funktionen synchronisieren. Fibre () Verwenden Sie await () und defer (). Wir rufen alle Methoden mit await () auf. Ersetzen Sie dann die Rückruffunktionen durch defer ().

Normaler Async-Code. Hierbei werden CallBack-Funktionen verwendet.

function add (var a, var b, function(err,res){
       console.log(res);
});

 function sub (var res2, var b, function(err,res1){
           console.log(res);
    });

 function div (var res2, var b, function(err,res3){
           console.log(res3);
    });

Synchronisieren Sie den obigen Code mit Fibre (), wait () und defer ()

fiber(function(){
     var obj1 = await(function add(var a, var b,defer()));
     var obj2 = await(function sub(var obj1, var b, defer()));
     var obj3 = await(function sub(var obj2, var b, defer()));

});

Ich hoffe das wird helfen. Danke

Mohan Ramakrishna
quelle
0

Heutzutage kann dieses Generatormuster in vielen Situationen eine Lösung sein.

Hier ein Beispiel für sequentielle Konsolenaufforderungen in nodejs mit der asynchronen Funktion readline.question:

var main = (function* () {

  // just import and initialize 'readline' in nodejs
  var r = require('readline')
  var rl = r.createInterface({input: process.stdin, output: process.stdout })

  // magic here, the callback is the iterator.next
  var answerA = yield rl.question('do you want this? ', r=>main.next(r))    

  // and again, in a sync fashion
  var answerB = yield rl.question('are you sure? ', r=>main.next(r))        

  // readline boilerplate
  rl.close()

  console.log(answerA, answerB)

})()  // <-- executed: iterator created from generator
main.next()     // kick off the iterator, 
                // runs until the first 'yield', including rightmost code
                // and waits until another main.next() happens
drodsou
quelle
-1

Sie sollten nicht darauf achten, was um den Anruf herum passiert , der die Faser erzeugt, sondern darauf, was innerhalb der Faser passiert . Sobald Sie sich in der Glasfaser befinden, können Sie synchron synchronisieren. Beispielsweise:

Funktion f1 () {
    console.log ('wait ...' + neues Datum);
    Schlaf (1000);
    console.log ('ok ...' + neues Datum);   
}}

Funktion f2 () {
    f1 ();
    f1 ();
}}

Faser (Funktion () {
    f2 ();
}).Lauf();

Innerhalb der Glasfaser, die Sie anrufen f1, f2und sleepals ob sie synchron wären.

In einer typischen Webanwendung erstellen Sie die Glasfaser in Ihrem HTTP-Anforderungs-Dispatcher. Sobald Sie dies getan haben, können Sie Ihre gesamte Anforderungsbearbeitungslogik im Synchronisierungsstil schreiben, auch wenn sie asynchrone Funktionen (fs, Datenbanken usw.) aufruft.

Bruno Jouhier
quelle
Danke Bruno. Aber was ist, wenn ich einen Synchronisierungsstil im Bootstrap-Code benötige, der ausgeführt werden muss, bevor der Server an den TCP-Port gebunden wird - z. B. Konfiguration oder Daten, die aus der asynchron geöffneten Datenbank gelesen werden müssen? Möglicherweise habe ich die gesamte server.js in Fibre eingeschlossen, und ich vermute, dass dadurch die Parallelität auf der gesamten Prozessebene beendet wird. Trotzdem ist es ein Vorschlag, der es wert ist, überprüft zu werden. Für mich sollte die ideale Lösung in der Lage sein, eine asynchrone Funktion zu verpacken, um eine Synchronisierungsaufrufsyntax bereitzustellen, und nur die nächsten Codezeilen in der Aufruferkette zu blockieren, ohne die Parallelität auf Prozessebene zu beeinträchtigen.
abbr
Sie können Ihren gesamten Bootstrap-Code in einen großen Fibre-Aufruf einbinden. Parallelität sollte kein Problem sein, da der Bootstrap-Code normalerweise vollständig ausgeführt werden muss, bevor Sie mit der Bearbeitung von Anforderungen beginnen. Außerdem verhindert eine Faser nicht, dass andere Fasern laufen: Jedes Mal, wenn Sie einen Ertragsruf ausführen, geben Sie anderen Fasern (und dem Hauptfaden) die Chance, zu laufen.
Bruno Jouhier
Ich habe die Express-Bootstrap-Datei server.js mit Glasfaser umwickelt. Die Ausführungssequenz ist das, wonach ich suche, aber dieser Wrap hat keine Auswirkung auf den Anforderungshandler. Ich denke, ich muss den gleichen Wrapper auf JEDEN Dispatcher anwenden. Ich habe an diesem Punkt aufgegeben, weil es nicht besser zu sein scheint, um ein globales Re-Factoring zu vermeiden. Mein Ziel ist es, die Auswirkungen auf den Anrufer zu minimieren, wenn die Datenerfassungsmethode in der DAO-Schicht von synchron auf asynchron geändert wird und Fibre die Herausforderung immer noch nicht erfüllt.
Abk.
@fred: Es macht nicht viel Sinn, Ereignisströme wie den Request-Handler zu "synchronisieren" - Sie müssten eine while(true) handleNextRequest()Schleife haben. Das Einwickeln jedes Anforderungshandlers in eine Faser würde.
Bergi
@fred: Fasern helfen Ihnen bei Express nicht viel, da der Express-Rückruf kein Fortsetzungsrückruf ist (ein Rückruf, der immer genau einmal aufgerufen wird, entweder mit einem Fehler oder mit einem Ergebnis). Aber Fasern lösen die Pyramide des Untergangs, wenn Sie viel Code auf asynchrone APIs mit Fortsetzungsrückrufen (wie fs, mongodb und viele andere) geschrieben haben.
Bruno Jouhier
-2

Ich hatte zuerst mit node.js zu kämpfen, und async.js ist die beste Bibliothek, die ich gefunden habe, um Ihnen dabei zu helfen. Wenn Sie synchronen Code mit dem Knoten schreiben möchten, gehen Sie wie folgt vor.

var async = require('async');

console.log('in main');

doABunchOfThings(function() {
  console.log('back in main');
});

function doABunchOfThings(fnCallback) {
  async.series([
    function(callback) {
      console.log('step 1');
      callback();
    },
    function(callback) {
      setTimeout(callback, 1000);
    },
    function(callback) {
      console.log('step 2');
      callback();
    },
    function(callback) {
      setTimeout(callback, 2000);
    },
    function(callback) {
      console.log('step 3');
      callback();
    },
  ], function(err, results) {
    console.log('done with things');
    fnCallback();
  });
}

Dieses Programm wird IMMER Folgendes produzieren ...

in main
step 1
step 2
step 3
done with things
back in main
Michael Connor
quelle
2
asyncfunktioniert in deinem Beispiel b / c es ist main, was Anrufer nicht interessiert. Stellen Sie sich vor, Ihr gesamter Code ist in eine Funktion eingeschlossen, die das Ergebnis eines Ihrer asynchronen Funktionsaufrufe zurückgeben soll. Es kann leicht durch Hinzufügen console.log('return');am Ende Ihres Codes überprüft werden, ob es nicht funktioniert . In diesem Fall erfolgt die Ausgabe von returnnach, in mainaber vorher step 1.
abbr
-11

Javascript ist eine einzelne Thread-Sprache, Sie möchten nicht Ihren gesamten Server blockieren! Async-Code eliminiert Rennbedingungen, indem Abhängigkeiten explizit gemacht werden.

Lernen Sie, asynchronen Code zu lieben!

Suchen Sie promisesnach asynchronem Code, ohne eine Pyramide der Rückrufhölle zu erstellen. Ich empfehle die VersprechenQ-Bibliothek für node.js.

httpGet(url.parse("http://example.org/")).then(function (res) {
    console.log(res.statusCode);  // maybe 302
    return httpGet(url.parse(res.headers["location"]));
}).then(function (res) {
    console.log(res.statusCode);  // maybe 200
});

http://howtonode.org/promises

BEARBEITEN: Dies ist bei weitem meine umstrittenste Antwort. Der Knoten verfügt jetzt über ein Schlüsselwort für den Ertrag, mit dem Sie asynchronen Code so behandeln können, als wäre er synchron. http://blog.alexmaccaw.com/how-yield-will-transform-node

roo2
quelle
1
Promise formuliert nur einen Rückrufparameter neu, anstatt die Funktion zu synchronisieren.
abbr
2
Sie möchten nicht, dass es synchronisiert wird, sonst blockiert Ihr gesamter Server! stackoverflow.com/questions/17959663/…
roo2
1
Wünschenswert ist ein Synchronisierungsaufruf, ohne andere Ereignisse zu blockieren, z. B. eine andere Anforderung, die von Node.js verarbeitet wird. Eine Synchronisierungsfunktion bedeutet per Definition nur, dass sie erst dann zum Anrufer zurückkehrt, wenn das Ergebnis vorliegt (nicht nur ein Versprechen). Der Server wird nicht von der Verarbeitung anderer Ereignisse ausgeschlossen, während der Anruf blockiert ist.
abbr
@fred: Ich denke du verpasst den Punkt der Versprechen . Sie sind nicht einfach eine Beobachtermusterabstraktion, sondern bieten eine Möglichkeit, asynchrone Aktionen zu verketten und zusammenzustellen.
Bergi
1
@Bergi, ich benutze viel Versprechen und weiß genau, was es tut. Tatsächlich wird nur ein einzelner Aufruf einer asynchronen Funktion in mehrere Aufrufe / Anweisungen aufgeteilt. Das Ergebnis ändert sich jedoch nicht. Wenn der Aufrufer zurückkehrt, kann er das Ergebnis der asynchronen Funktion nicht zurückgeben. Schauen Sie sich das Beispiel an, das ich in JSFiddle gepostet habe. Der Aufrufer ist in diesem Fall die Funktion AnticipatedSyncFunction und die asynchrone Funktion ist setTimeout. Wenn Sie meine Herausforderung mit einem Versprechen beantworten können, zeigen Sie es mir bitte.
Abk.