So schreiben Sie asynchrone Funktionen für Node.js.

114

Ich habe versucht zu untersuchen, wie genau asynchrone Funktionen geschrieben werden sollen. Nachdem ich viel dokumentiert habe, ist es mir immer noch unklar.

Wie schreibe ich asynchrone Funktionen für Node? Wie soll ich die Fehlerereignisbehandlung korrekt implementieren?

Eine andere Möglichkeit, meine Frage zu stellen, wäre folgende: Wie soll ich die folgende Funktion interpretieren?

var async_function = function(val, callback){
    process.nextTick(function(){
        callback(val);
    });
};

Außerdem fand ich diese Frage zu SO ("Wie erstelle ich eine nicht blockierende asynchrone Funktion in node.js?") Interessant. Ich habe noch nicht das Gefühl, dass es beantwortet wurde.

Kriem
quelle
14
Deshalb frage ich. Mir ist nicht klar, wie unterschiedlich diese Funktionen sind.
Kriem
Ich empfehle Ihnen, sich Ihren Lieblingsbrowser anzusehen setTimeoutund damit setIntervalherumzuspielen. Oder Ajax-Rückrufe (wahrscheinlich am nächsten an der Knotenerfahrung) oder Ereignis-Listener für Dinge, mit denen Sie vertraut sind, wie Klick- und Ladeereignisse. Das asynchrone Modell ist bereits im Browser vorhanden und im Knoten genau gleich.
David
@davin - Ich schätze, ich verstehe das asynchrone Modell dann nicht vollständig.
Kriem
@Kriem, ich habe gestern etwas beantwortet, das helfen könnte: stackoverflow.com/questions/6883648/… Es ist keine Antwort auf Ihre Frage, aber es ist themenbezogen . Versuchen Sie, die Frage und Antwort dort zu lesen und zu spielen, und spielen Sie mit dem Code, um zu verstehen, was los ist.
David
2
@ Raynos Was ist die Definition von "asynchroner Funktion"?
Anderson Green

Antworten:

85

Sie scheinen asynchrone E / A mit asynchronen Funktionen zu verwechseln. node.js verwendet asynchrone nicht blockierende E / A, da nicht blockierende E / A besser sind. Der beste Weg, es zu verstehen, ist, sich einige Videos von Ryan Dahl anzusehen.

Wie schreibe ich asynchrone Funktionen für Node?

Schreiben Sie einfach normale Funktionen. Der einzige Unterschied besteht darin, dass sie nicht sofort ausgeführt, sondern als Rückrufe weitergegeben werden.

Wie soll ich die Fehlerereignisbehandlung korrekt implementieren?

Im Allgemeinen geben APIs einen Rückruf mit einem Fehler als erstem Argument. Beispielsweise

database.query('something', function(err, result) {
  if (err) handle(err);
  doSomething(result);
});

Ist ein allgemeines Muster.

Ein weiteres häufiges Muster ist on('error'). Beispielsweise

process.on('uncaughtException', function (err) {
  console.log('Caught exception: ' + err);
});

Bearbeiten:

var async_function = function(val, callback){
    process.nextTick(function(){
        callback(val);
    });
};

Die obige Funktion beim Aufruf als

async_function(42, function(val) {
  console.log(val)
});
console.log(43);

Druckt 42asynchron an die Konsole. Insbesondere process.nextTickwird ausgelöst, nachdem der aktuelle Eventloop-Callstack leer ist. Dieser Aufrufstapel ist nach async_functionund leerconsole.log(43) ausgeführt worden. Also drucken wir 43 gefolgt von 42.

Sie sollten wahrscheinlich etwas in der Ereignisschleife lesen.

Raynos
quelle
Ich habe die Dahl-Videos gesehen, aber ich scheine die Angelegenheit nicht zu verstehen, fürchte ich. :(
Kriem
1
@Kriem siehe aktualisierte Antwort und lesen Sie über die Ereignisschleife
Raynos
1
Vielen Dank für die Erkenntnisse. Ich bin mir jetzt bewusster, was mir an Wissen fehlt. :) Dein letztes Beispiel hat übrigens geholfen.
Kriem
Ich denke, Ihre Aussage über asynchrone E / A ist "besser" ist zu allgemein. In diesem Sinne ja, aber insgesamt ist das möglicherweise nicht der Fall.
Jake B
In Ihrem ersten Codebeispiel überprüfen Sie das Argument err, sind aber danach nicht zurückgekehrt. Im Fehlerfall wird der Code fortgesetzt und verursacht möglicherweise schwerwiegende Probleme in Ihrer Anwendung.
Gabriel McAdams
9

Es reicht nicht aus, nur an Rückrufen vorbeizukommen. Sie müssen beispielsweise Settimer verwenden, um die Funktion asynchron zu machen.

Beispiele: Nicht asynchrone Funktionen:

function a() {
  var a = 0;    
  for(i=0; i<10000000; i++) {
    a++;
  };
  b();
};

function b() {
  var a = 0;    
  for(i=0; i<10000000; i++) {
    a++;
  };    
  c();
};

function c() {
  for(i=0; i<10000000; i++) {
  };
  console.log("async finished!");
};

a();
console.log("This should be good");

Wenn Sie das obige Beispiel ausführen, sollte dies gut sein und warten, bis diese Funktionen funktionieren.

Pseudo-Multithread-Funktionen (asynchron):

function a() {
  setTimeout ( function() {
    var a = 0;  
    for(i=0; i<10000000; i++) {
      a++;
    };
    b();
  }, 0);
};

function b() {
  setTimeout ( function() {
    var a = 0;  
    for(i=0; i<10000000; i++) {
      a++;
    };  
    c();
  }, 0);
};

function c() {
  setTimeout ( function() {
    for(i=0; i<10000000; i++) {
    };
    console.log("async finished!");
  }, 0);
};

a();
console.log("This should be good");

Dieser wird völlig asynchron sein. Dies sollte gut sein, wird geschrieben, bevor Async beendet ist.

Beruhigungsvogel
quelle
3

Wenn Sie wissen, dass eine Funktion ein Versprechen zurückgibt, empfehle ich die Verwendung der neuen asynchronen / wartenden Funktionen in JavaScript. Dadurch sieht die Syntax synchron aus, funktioniert jedoch asynchron. Wenn Sie das asyncSchlüsselwort zu einer Funktion hinzufügen , können Sie awaitin diesem Bereich Versprechen abgeben:

async function ace() {
  var r = await new Promise((resolve, reject) => {
    resolve(true)
  });

  console.log(r); // true
}

Wenn eine Funktion kein Versprechen zurückgibt, empfehle ich, es in ein neues Versprechen zu verpacken, das Sie definieren, und dann die gewünschten Daten aufzulösen:

function ajax_call(url, method) {
  return new Promise((resolve, reject) => {
    fetch(url, { method })
    .then(resp => resp.json())
    .then(json => { resolve(json); })
  });
}

async function your_function() {
  var json = await ajax_call('www.api-example.com/some_data', 'GET');
  console.log(json); // { status: 200, data: ... }
}

Fazit: Nutzen Sie die Kraft der Versprechen.

ryanwaite28
quelle
Hier ist zu beachten, dass das Versprechen immer noch synchron ausgeführt wird.
shadow0359
2

Versuchen Sie dies, es funktioniert sowohl für den Knoten als auch für den Browser.

isNode = (typeof exports !== 'undefined') &&
(typeof module !== 'undefined') &&
(typeof module.exports !== 'undefined') &&
(typeof navigator === 'undefined' || typeof navigator.appName === 'undefined') ? true : false,
asyncIt = (isNode ? function (func) {
  process.nextTick(function () {
    func();
  });
} : function (func) {
  setTimeout(func, 5);
});
Pradeep
quelle
18
4 Abstimmungen und nicht einmal ein konstruktiver Kommentar ..: \
Omer
6
@Omer So ist das Leben auf SO.
Stück Digital
6
@NorbertoBezi Vielleicht ist der Code für Sie selbsterklärend, aber nicht für denjenigen, der die Antwort gepostet hat. Deshalb ist es immer eine gute Praxis, dies beim Downvoting zu erklären.
Omer
0

Ich habe zu viele Stunden für eine solche Aufgabe in node.js verbracht. Ich bin hauptsächlich Front-End-Typ.

Ich finde das ziemlich wichtig, da alle asynchronen Knotenmethoden mit Rückrufen umgehen und es besser ist, sie in Promise umzuwandeln.

Ich möchte nur ein mögliches Ergebnis zeigen, schlanker und lesbarer. Wenn Sie ECMA-6 mit Async verwenden, können Sie es so schreiben.

 async function getNameFiles (dirname) {
  return new Promise((resolve, reject) => {
    fs.readdir(dirname, (err, filenames) => {
      err !== (undefined || null) ? reject(err) : resolve(filenames)
    })
  })
}

Das (undefined || null)ist für Repl-Szenarien (Read Event Print Loop), bei denen undefined ebenfalls funktioniert.

Yoarthur
quelle