webentwicklung-frage-antwort-db.com.de

Wie man eine Funktion warten lässt, bis ein Callback mit node.js aufgerufen wurde

Ich habe eine vereinfachte Funktion, die so aussieht:

function(query) {
  myApi.exec('SomeCommand', function(response) {
    return response;
  });
}

Grundsätzlich möchte ich, dass es myApi.exec aufruft und die Antwort zurückgibt, die im Rückruf-Lambda angegeben ist. Der obige Code funktioniert jedoch nicht und kehrt einfach sofort zurück.

Nur für einen sehr hackigen Versuch habe ich das Folgende ausprobiert, was nicht funktioniert hat, aber zumindest hast du die Idee, was ich erreichen will:

function(query) {
  var r;
  myApi.exec('SomeCommand', function(response) {
    r = response;
  });
  while (!r) {}
  return r;
}

Was ist im Grunde eine gute 'node.js/event driven' Vorgehensweise? Ich möchte, dass meine Funktion wartet, bis der Rückruf aufgerufen wird, und dann den Wert zurückgibt, der an sie übergeben wurde.

239
Chris

Die Methode "good node.js/event driven" ist nicht warten.

Wie fast alles andere bei der Arbeit mit ereignisgesteuerten Systemen wie Node sollte Ihre Funktion einen Callback-Parameter akzeptieren, der aufgerufen wird, wenn die Berechnung abgeschlossen ist. Der Aufrufer sollte nicht warten, bis der Wert im normalen Sinne "zurückgegeben" wird, sondern die Routine senden, die den resultierenden Wert verarbeitet:

function(query, callback) {
  myApi.exec('SomeCommand', function(response) {
    // other stuff here...
    // bla bla..
    callback(response); // this will "return" your value to the original caller
  });
}

Also benutzt du es nicht so:

var returnValue = myFunction(query);

Aber so:

myFunction(query, function(returnValue) {
  // use the return value here instead of like a regular (non-evented) return value
});
265
Jakob

überprüfen Sie dies: https://github.com/luciotato/waitfor-ES6

deinen Code mit wait.for: (benötigt Generatoren, --harmony flag)

function* (query) {
  var r = yield wait.for( myApi.exec, 'SomeCommand');
  return r;
}
23
Lucio M. Tato

Eine Möglichkeit, dies zu erreichen, besteht darin, den API-Aufruf in ein Versprechen einzuwickeln und dann mit await auf das Ergebnis zu warten.

// let's say this is the API function with two callbacks,
// one for success and the other for error
function apiFunction(query, successCallback, errorCallback) {
    if (query == "bad query") {
        errorCallback("problem with the query");
    }
    successCallback("Your query was <" + query + ">");
}

// myFunction wraps the above API call into a Promise
// and handles the callbacks with resolve and reject
function apiFunctionWrapper(query) {
    return new Promise((resolve, reject) => {
        apiFunction(query,(successResponse) => {
            resolve(successResponse);
        }, (errorResponse) => {
            reject(errorResponse)
        });
    });
}

// now you can use await to get the result from the wrapped api function
// and you can use standard try-catch to handle the errors
async function businessLogic() {
    try {
        const result = await apiFunctionWrapper("query all users");
        console.log(result);

        // the next line will fail
        const result2 = await apiFunctionWrapper("bad query");
    } catch(error) {
        console.error("ERROR:" + error);
    }
}

// call the main function
businessLogic();

Ausgabe:

Your query was <query all users>
ERROR:problem with the query
20
Timo

Wenn Sie den Rückruf nicht verwenden möchten, können Sie das Modul "Q" verwenden.

Zum Beispiel:

function getdb() {
    var deferred = Q.defer();
    MongoClient.connect(databaseUrl, function(err, db) {
        if (err) {
            console.log("Problem connecting database");
            deferred.reject(new Error(err));
        } else {
            var collection = db.collection("url");
            deferred.resolve(collection);
        }
    });
    return deferred.promise;
}


getdb().then(function(collection) {
   // This function will be called afte getdb() will be executed. 

}).fail(function(err){
    // If Error accrued. 

});

Weitere Informationen finden Sie hier: https://github.com/kriskowal/q

10
vishal patel

Wenn Sie möchten, dass es sehr einfach und unkompliziert ist, ohne ausgefallene Bibliotheken darauf zu warten, dass Callback-Funktionen in Node ausgeführt werden, bevor Sie einen anderen Code ausführen, gehen Sie folgendermaßen vor:

//initialize a global var to control the callback state
var callbackCount = 0;
//call the function that has a callback
someObj.executeCallback(function () {
    callbackCount++;
    runOtherCode();
});
someObj2.executeCallback(function () {
    callbackCount++;
    runOtherCode();
});

//call function that has to wait
continueExec();

function continueExec() {
    //here is the trick, wait until var callbackCount is set number of callback functions
    if (callbackCount < 2) {
        setTimeout(continueExec, 1000);
        return;
    }
    //Finally, do what you need
    doSomeThing();
}
9
Marquinho Peli

Hinweis: Diese Antwort sollte wahrscheinlich nicht im Produktionscode verwendet werden. Es ist ein Hack und Sie sollten über die Auswirkungen Bescheid wissen.

Es gibt das vrun -Modul (aktualisiert für neuere Nodejs-Versionen hier ), mit dem Sie eine einzelne Schleife der libuv-Hauptereignisschleife (die die Nodejs-Hauptschleife ist) ausführen können.

Ihr Code würde so aussehen:

function(query) {
  var r;
  myApi.exec('SomeCommand', function(response) {
    r = response;
  });
  var uvrun = require("uvrun");
  while (!r)
    uvrun.runOnce();
  return r;
}

(Alternativ können Sie auch uvrun.runNoWait() verwenden. Dies könnte Probleme beim Blockieren vermeiden, beansprucht jedoch 100% der CPU.)

Man beachte, dass dieser Ansatz den gesamten Zweck von Nodejs ungültig macht, d. H. Alles asynchron und nicht blockierend zu machen. Außerdem kann dies zu einer erheblichen Erhöhung der Callstack-Tiefe führen, sodass möglicherweise Stapelüberläufe auftreten. Wenn Sie eine solche Funktion rekursiv ausführen, treten auf jeden Fall Probleme auf.

In den anderen Antworten erfahren Sie, wie Sie Ihren Code so umgestalten, dass er "richtig" funktioniert.

Diese Lösung hier ist wahrscheinlich nur nützlich, wenn Sie testen und insbes. synchronisiert und Seriennummer haben wollen.

5
Albert

Seit Knoten 4.8.0 können Sie die Funktion von ES6 verwenden, die als Generator bezeichnet wird. Sie können diesem Artikel für tiefere Konzepte folgen. Aber im Grunde kann man Generatoren und Versprechungen verwenden, um diesen Job zu erledigen. Ich benutze Bluebird , um den Generator zu versprechen und zu verwalten.

Ihr Code sollte in Ordnung sein, wie im folgenden Beispiel.

const Promise = require('bluebird');

function* getResponse(query) {
  const r = yield new Promise(resolve => myApi.exec('SomeCommand', resolve);
  return r;
}

Promise.coroutine(getResponse)()
  .then(response => console.log(response));
4
Douglas Soares

Bei mir hat es geklappt zu nutzen

JSON.parse(result)['key']

auf das ergebnis habe ich gerechnet. Dies mag nicht "super general" sein, aber am Ende hat "JSON.parse" das Warten auf den asynchronen Aufruf verwaltet, während result['key'] dies nicht tat.

1
MrMuc Mic

angenommen, Sie haben eine Funktion:

var fetchPage(page, callback) {
   ....
   request(uri, function (error, response, body) {
        ....
        if (something_good) {
          callback(true, page+1);
        } else {
          callback(false);
        }
        .....
   });


};

sie können Rückrufe wie folgt verwenden:

fetchPage(1, x = function(next, page) {
if (next) {
    console.log("^^^ CALLBACK -->  fetchPage: " + page);
    fetchPage(page, x);
}
});
1
Z0LtaR
exports.dbtest = function (req, res) {
  db.query('SELECT * FROM users', [], res, renderDbtest);
};

function renderDbtest(result, res){
  res.render('db', { result: result });
}

Hier ist, wie ich es gemacht habe, Sie müssen nur "res" damit übergeben, damit Sie später rendern können

0
harmony

Das vereitelt den Zweck, IO nicht zu blockieren - Sie blockieren es, wenn es nicht geblockt werden muss :)

Sie sollten Ihre Rückrufe verschachteln, anstatt node.js zum Warten zu zwingen, oder einen anderen Rückruf innerhalb des Rückrufs aufrufen, in dem Sie das Ergebnis von r benötigen.

Wenn Sie das Blockieren erzwingen müssen, denken Sie wahrscheinlich über eine falsche Architektur nach.

0
user193476