Ich bin ziemlich neu bei Node.js (habe erst diese Woche angefangen) und es gibt ein grundlegendes Stück, das ich nur schwer verstehen kann. Ich habe eine Hilfsfunktion, die einen MySQL-Datenbankaufruf ausführt, um ein paar Informationen zu erhalten. Ich verwende dann eine Rückruffunktion, um diese Daten an den Anrufer zurückzuleiten, was gut funktioniert, aber wenn ich diese Daten außerhalb dieses Rückrufs verwenden möchte, stoße ich auf Probleme. Hier ist der Code:

    /** Helper Function **/
    function getCompanyId(token, callback) {
            var query = db.query('SELECT * FROM companies WHERE token = ?', token, function(err, result) {
                var count = Object.keys(result).length;

                if(count == 0) {
                    return;
                } else {
                    callback(null, result[0].api_id);
                }
            });
        }

/*** Function which uses the data from the helper function ***/
    api.post('/alert', function(request, response) {
        var data = JSON.parse(request.body.data);
        var token = data.token;

        getCompanyId(token, function(err, result) {
            // this works
            console.log(result);
        });

        // the problem is that I need result here so that I can use it else where in this function.
    });

Wie Sie sehen, habe ich Zugriff auf den Rückgabewert von getCompanyId (), solange ich im Bereich des Rückrufs bleibe, diesen Wert jedoch außerhalb des Rückrufs verwenden muss. Ich konnte dies in einer anderen Funktion umgehen, indem ich einfach die gesamte Logik in diesen Rückruf steckte, aber das wird in diesem Fall nicht funktionieren. Jeder Einblick, wie dies besser strukturiert werden kann, wäre sehr willkommen. Bisher genieße ich Node.js wirklich, aber offensichtlich habe ich viel zu lernen.

2
Nicholas Kreidberg 6 Dez. 2013 im 00:36

4 Antworten

Beste Antwort

Kurze Antwort - Sie können dies nicht tun, ohne die asynchrone Natur von Node.js zu verletzen.

Denken Sie über die Konsequenzen nach, wenn Sie versuchen, außerhalb Ihres Rückrufs auf result zuzugreifen. Wenn Sie diesen Wert verwenden müssen und der Rückruf noch nicht ausgeführt wurde, was werden Sie dann tun? Sie können nicht sleep warten, bis der Wert festgelegt ist - dies ist nicht kompatibel mit dem ereignisgesteuerten Single-Threaded-Design von Node. Ihr gesamtes Programm müsste die Ausführung beenden, während Sie auf die Ausführung des Rückrufs warten.

Jeder Code, der von result abhängt, sollte sich im Rückruf getCompanyId befinden:

api.post('/alert', function(request, response) {
    var data = JSON.parse(request.body.data);
    var token = data.token;

    getCompanyId(token, function(err, result) {
        //Any logic that depends on result has to be nested in here
    });
});

Einer der schwierigsten Teile beim Erlernen von Node.js (und die asynchrone Programmierung ist allgemein) ist das Erlernen des asynchronen Denkens. Es kann zunächst schwierig sein, aber es lohnt sich, weiterzumachen. Sie können versuchen, prozedural zu kämpfen und zu codieren, aber dies führt unweigerlich zu nicht wartbarem, verschlungenem Code.

Wenn Ihnen die Idee mehrerer verschachtelter Rückrufe nicht gefällt, können Sie sich Versprechen ansehen, mit denen Sie Methoden verketten können, anstatt sie zu verschachteln. Dieser Artikel ist ein Gute Einführung in Q, eine Umsetzung von Versprechungen.

5
joews 5 Dez. 2013 im 21:01

Wenn Sie Rückrufgerüche vermeiden möchten, müssen Sie die Event Emitter Class von Node wie folgt verwenden:

Oben in der Datei erfordern Ereignismodul -

var emitter = require('events').EventEmitter();

Dann in Ihrem Rückruf:

api.post('/alert', function(request, response) {
    var data = JSON.parse(request.body.data);
    var token = data.token;

    getCompanyId(token, function(err, result) {
        // this works
        console.log(result);
        emitter.emit('company:id:returned', result);
    });

    // the problem is that I need result here so that I can use it else where in this function.
});

then after your function you can use the on method anywhere like so:

getCompanyId(token, function(err, result) {
        // this works
        console.log(result);
        emitter.emit('company:id:returned', result);
    });

    // the problem is that I need result here so that I can use it else where in this function.
    emitter.on('company:id:returned', function(results) {
       // do what you need with results
    });

Achten Sie nur darauf, gute Namespace-Konventionen für Ihre Events einzurichten, damit Sie nicht über Ereignisse durcheinander geraten. Außerdem sollten Sie die Anzahl der Listener beobachten, die Sie anhängen. Hier ist ein guter Link als Referenz:

http://www.sitepoint.com/nodejs-events-and-eventemitter/

1
kkemple 5 Dez. 2013 im 21:06

Wenn Sie befürchten, dass alles in der Rückruffunktion überfüllt ist, können Sie die Funktion jederzeit benennen, verschieben und die Funktion dann als Rückruf übergeben:

getCompanyId(token, doSomethingAfter); // Pass the function in

function doSomethingAfter(err, result) {
    // Code here
}
2
matth 5 Dez. 2013 im 20:41

Mein "Aha" -Moment kam, als ich anfing, diese als "Feuer und Vergessen" -Methoden zu betrachten. Suchen Sie nicht nach Rückgabewerten, die von den Methoden zurückkommen, da sie nicht zurückkommen. Der aufrufende Code sollte fortgesetzt werden oder einfach enden. Ja, es fühlt sich komisch an.

Wie @joews sagt, müssen Sie alles, was von diesem Wert abhängt, in die Rückrufe einfügen.

Dies erfordert häufig die Übergabe zusätzlicher Parameter. Wenn Sie beispielsweise eine typische HTTP-Anforderung / Antwort ausführen, planen Sie, die Antwort jeden Schritt entlang der Rückrufkette nach unten zu senden. Der letzte Rückruf setzt (hoffentlich) Daten in der Antwort oder setzt einen Fehlercode und sendet sie dann an den Benutzer zurück.

2
user949300 5 Dez. 2013 im 21:03