Suche…


Einführung

Node ist eine Programmiersprache, in der alles asynchron laufen kann. Nachfolgend finden Sie einige Beispiele und die typischen Aspekte des asynchronen Arbeitens.

Syntax

  • doSomething ([args], function ([argsCB]) {/ * tun etwas, wenn sie fertig sind * /});
  • doSomething ([args], ([argsCB]) => {/ * tun Sie etwas, wenn Sie fertig sind * /});

Rückruffunktionen

Callback-Funktionen in JavaScript

Rückruffunktionen sind in JavaScript üblich. Callback-Funktionen sind in JavaScript möglich, da Funktionen erstklassige Bürger sind .

Synchrone Rückrufe.

Rückruffunktionen können synchron oder asynchron sein. Da asynchrone Callback-Funktionen komplexer sein können, finden Sie hier ein einfaches Beispiel für eine synchrone Callback-Funktion.

// a function that uses a callback named `cb` as a parameter
function getSyncMessage(cb) {
    cb("Hello World!");
}

console.log("Before getSyncMessage call");
// calling a function and sending in a callback function as an argument.
getSyncMessage(function(message) {
    console.log(message);
});
console.log("After getSyncMessage call");

Die Ausgabe für den obigen Code lautet:

> Before getSyncMessage call
> Hello World!
> After getSyncMessage call

Zuerst gehen wir Schritt für Schritt durch, wie der obige Code ausgeführt wird. Dies ist mehr für diejenigen, die das Konzept von Rückrufen nicht bereits verstehen, wenn Sie es bereits verstanden haben. Sie können diesen Absatz gerne überspringen. Zuerst wird der Code analysiert und dann wird zunächst die Zeile 6 ausgeführt, die Before getSyncMessage call an die Konsole Before getSyncMessage call . Dann Linie 8 ausgeführt, der die Funktion aufruft getSyncMessage in einer anonymen Funktion als Argument für den Parameter namens Senden cb in der getSyncMessage Funktion. Die Ausführung erfolgt nun in der Funktion getSyncMessage in Zeile 3, die die soeben übergebene Funktion cb ausführt. Dieser Aufruf sendet eine Argumentzeichenfolge "Hello World" für die mit Parameter bezeichnete message in der in anonymous übergebenen Funktion. Die Ausführung geht dann zu Zeile 9, die Hello World! zur Konsole. Dann durchläuft die Ausführung den Prozess des Verlassens des Callstacks ( siehe auch ), wobei die Zeile 10, dann die Zeile 4 und dann schließlich die Zeile 11 wieder erreicht werden.

Einige Informationen, die Sie im Allgemeinen über Rückrufe kennen sollten:

  • Die Funktion, die Sie als Callback an eine Funktion senden, kann null, einmal oder mehrmals aufgerufen werden. Auf die Umsetzung kommt es an.
  • Die Callback-Funktion kann synchron oder asynchron und möglicherweise sowohl synchron als auch asynchron aufgerufen werden.
  • Wie bei normalen Funktionen sind die Namen, die Sie Ihrer Funktion geben, nicht wichtig, aber die Reihenfolge ist. In Zeile 8 könnte die Parameter message beispielsweise mit dem Namen statement , msg oder, wenn Sie unsinnig sind, so etwas wie jellybean . Sie sollten also wissen, welche Parameter in Ihrem Callback gesendet werden, damit Sie sie mit den richtigen Namen in der richtigen Reihenfolge anzeigen können.

Asynchrone Rückrufe.

Zu beachten ist, dass JavaScript standardmäßig synchron ist. In der Umgebung (API, Browser, Node.js usw.) gibt es jedoch APIs, die es asynchron machen könnten (mehr dazu hier ).

Einige häufige Dinge, die in JavaScript-Umgebungen, die Rückrufe akzeptieren, asynchron sind:

  • Veranstaltungen
  • setTimeout
  • setInterval
  • die Abruf-API
  • Versprechen

Jede Funktion, die eine der oben genannten Funktionen verwendet, kann mit einer Funktion umschlossen werden, die einen Rückruf übernimmt, und der Rückruf wäre dann ein asynchroner Rückruf (obwohl das Versprechen eines Versprechens mit einer Funktion, die einen Rückruf entgegennimmt, wahrscheinlich als Anti-Muster betrachtet wird.) es gibt mehr bevorzugte Wege, um Versprechen zu behandeln).

In Anbetracht dieser Informationen können wir eine asynchrone Funktion ähnlich der obigen synchronen Funktion erstellen.

// a function that uses a callback named `cb` as a parameter
function getAsyncMessage(cb) {
    setTimeout(function () { cb("Hello World!") }, 1000);
}

console.log("Before getSyncMessage call");
// calling a function and sending in a callback function as an argument.
getAsyncMessage(function(message) {
    console.log(message);
});
console.log("After getSyncMessage call");

Welche druckt Folgendes auf die Konsole:

> Before getSyncMessage call
> After getSyncMessage call
// pauses for 1000 ms with no output
> Hello World!

Die Zeilenausführung geht an die Protokolle der Zeile 6 "Vor dem Aufruf von getSyncMessage". Dann wird die Ausführung in Zeile 8 mit dem Aufruf von getAsyncMessage und einem Rückruf für den Parameter cb . Dann wird Zeile 3 ausgeführt, die setTimeout mit einem Callback als erstem Argument und der Nummer 300 als zweitem Argument aufruft. setTimeout tut alles, was es tut, und hält an diesem Callback fest, sodass es später in 1000 Millisekunden aufgerufen werden kann. setTimeout das Timeout eingerichtet und die 1000 Millisekunden angehalten wurde, übergibt es die Ausführung an die Stelle, an der es unterbrochen wurde, und geht in Zeile 4 weiter , dann die Leitung 11, und dann pausiert für 1 Sekunde und setTimeout ruft dann seine Rückruffunktion , die 3 die Ausführung zurück auf Linie nimmt , wo getAsyncMessages Rückruf mit dem Wert „Hallo , Welt“ für seine Parameter aufgerufen wird message , die dann auf die Konsole auf der Leitung angemeldet ist 9 .

Rückruffunktionen in Node.js

NodeJS verfügt über asynchrone Callbacks und stellt normalerweise zwei Parameter für Ihre Funktionen bereit, die üblicherweise als err und data . Ein Beispiel zum Lesen eines Dateitextes.

const fs = require("fs");

fs.readFile("./test.txt", "utf8", function(err, data) {
    if(err) {
        // handle the error 
    } else {
        // process the file text given with data
    }
});

Dies ist ein Beispiel für einen Rückruf, der einmal aufgerufen wird.

Es ist empfehlenswert, den Fehler irgendwie zu behandeln, selbst wenn Sie ihn nur protokollieren oder werfen. Das Andernfalls ist nicht erforderlich, wenn Sie werfen oder zurückkehren, und es kann entfernt werden, um die Einrückung zu verringern, solange Sie die Ausführung der aktuellen Funktion im if-Vorgang beenden, indem Sie etwa das Werfen oder das Zurückkehren ausführen.

Obwohl es häufig üblich ist, err zu sehen, können data nicht immer der Fall sein, dass Ihre Rückrufe dieses Muster verwenden. Am besten sollten Sie sich die Dokumentation ansehen.

Ein weiterer Beispielrückruf stammt aus der Express-Bibliothek (Express 4.x):

// this code snippet was on http://expressjs.com/en/4x/api.html
const express = require('express');
const app = express();

// this app.get method takes a url route to watch for and a callback
// to call whenever that route is requested by a user.
app.get('/', function(req, res){
  res.send('hello world');
});

app.listen(3000);

Dieses Beispiel zeigt einen Rückruf, der mehrmals aufgerufen wird. Der Rückruf verfügt über zwei Objekte als Parameter, die hier als req und res Diese Namen entsprechen jeweils der Anforderung und der Antwort. Sie bieten Möglichkeiten, die eingehende Anforderung anzuzeigen und die Antwort festzulegen, die an den Benutzer gesendet wird.

Wie Sie sehen, gibt es verschiedene Möglichkeiten, mit Hilfe eines Rückrufs Sync- und Asynchron-Code in JavaScript auszuführen, und Callbacks sind in JavaScript sehr allgegenwärtig.

Code-Beispiel

Frage: Wie wird der Code ausgegeben und warum?

setTimeout(function() {
    console.log("A");
}, 1000);

setTimeout(function() {
    console.log("B");
}, 0);

getDataFromDatabase(function(err, data) {
    console.log("C");
    setTimeout(function() {
        console.log("D");
    }, 1000);
});

console.log("E");

Ausgabe: Dies ist sicher bekannt: EBAD . C ist unbekannt, wann es protokolliert wird.

Erläuterung: Der Compiler wird bei den setTimeout und getDataFromDatabase nicht getDataFromDatabase . Die erste Zeile, die er protokolliert, ist E Die Callback-Funktionen (erstes Argument von setTimeout ) werden nach dem gesetzten Timeout asynchron ausgeführt!

Mehr Details:

  1. E hat kein setTimeout
  2. B hat ein Zeitlimit von 0 Millisekunden
  3. A hat ein festgelegtes Timeout von 1000 Millisekunden
  4. D müssen eine Datenbank anfordern, nachdem es muss D 1000 Millisekunden warten , so dass es nach kommt A .
  5. C ist unbekannt, weil es unbekannt ist, wenn die Daten der Datenbank angefordert werden. Es könnte vor oder nach A .

Async-Fehlerbehandlung

Versuchen Sie, zu fangen

Fehler müssen immer behandelt werden. Wenn Sie eine synchrone Programmierung verwenden, können Sie einen try catch . Das funktioniert aber nicht, wenn Sie asynchron arbeiten! Beispiel:

try {
    setTimeout(function() {
        throw new Error("I'm an uncaught error and will stop the server!");
    }, 100); 
}
catch (ex) {
    console.error("This error will not be work in an asynchronous situation: " + ex);
}

Async-Fehler werden nur innerhalb der Rückruffunktion behandelt!

Arbeitsmöglichkeiten

v0,8

Event-Handler

Die ersten Versionen von Node.JS erhielten einen Event-Handler.

process.on("UncaughtException", function(err, data) { 
    if (err) {
        // error handling
    }
});
v0,8

Domains

Innerhalb einer Domain werden die Fehler über die Ereignis-Emitter freigegeben. Dadurch werden alle Fehler, Timer und Callback-Methoden implizit nur innerhalb der Domäne registriert. Durch einen Fehler ein Fehlerereignis senden und die Anwendung nicht zum Absturz bringen.

var domain = require("domain");
var d1 = domain.create();
var d2 = domain.create();

d1.run(function() {
    d2.add(setTimeout(function() {
        throw new Error("error on the timer of domain 2");
    }, 0));
});

d1.on("error", function(err) {
    console.log("error at domain 1: " + err);
});

d2.on("error", function(err) {
    console.log("error at domain 2: " + err);
});

Rückruf Hölle

Callback Hell (auch eine Pyramide des Untergangs oder Bumerang-Effekts) entsteht, wenn Sie zu viele Callback-Funktionen in eine Callback-Funktion einbetten. Hier ein Beispiel zum Lesen einer Datei (in ES6).

const fs = require('fs');
let filename = `${__dirname}/myfile.txt`;

fs.exists(filename, exists => {
    if (exists) {
        fs.stat(filename, (err, stats) => {
            if (err) {
                throw err;
            }
            if (stats.isFile()) {
                fs.readFile(filename, null, (err, data) => {
                    if (err) {
                        throw err;
                    }
                    console.log(data);
                });
            }
            else {
                throw new Error("This location contains not a file");
            }
        });
    }
    else {
        throw new Error("404: file not found");
    }
}); 

So vermeiden Sie "Callback Hell"

Es wird empfohlen, nicht mehr als 2 Rückruffunktionen zu verschachteln. Dies hilft Ihnen, die Lesbarkeit des Codes aufrechtzuerhalten, und ist in Zukunft viel einfacher zu pflegen. Wenn Sie mehr als zwei Rückrufe verschachteln müssen, verwenden Sie stattdessen verteilte Ereignisse .

Es gibt auch eine Bibliothek namens async , mit der Callbacks und deren Ausführung verwaltet werden können, die auf npm verfügbar sind. Es erhöht die Lesbarkeit des Rückrufcodes und gibt Ihnen mehr Kontrolle über Ihren Callback-Code-Fluss, einschließlich der Möglichkeit, diese parallel oder hintereinander auszuführen.

Native Versprechungen

v6.0.0

Versprechen sind ein Werkzeug für die asynchrone Programmierung. In JavaScript sind Versprechungen für ihre then Methoden bekannt. Versprechen haben zwei Hauptzustände "ausstehend" und "erledigt". Sobald ein Versprechen „erledigt“ ist, kann es nicht wieder „ausstehend“ sein. Dies bedeutet, dass Versprechen meistens für Ereignisse gut sind, die nur einmal vorkommen. Der "erledigte" Staat hat zwei Zustände, die auch "gelöst" und "abgelehnt" sind. Sie können ein neues Versprechen erstellen, indem Sie das new Schlüsselwort verwenden und eine Funktion an den Konstruktor new Promise(function (resolve, reject) {}) .

Die an den Promise-Konstruktor übergebene Funktion erhält immer einen ersten und einen zweiten Parameter, die üblicherweise als resolve und reject bezeichnet werden. Die Benennung dieser beiden Parameter ist üblich, aber sie versprechen das Versprechen entweder in den Zustand "gelöst" oder "abgelehnt". Wenn eines dieser beiden genannt wird, geht das Versprechen von "ausstehend" auf "erledigt" über. resolve wird aufgerufen , wenn die gewünschte Aktion, die oft asynchron ist, durchgeführt worden ist und reject verwendet wird , wenn die Aktion errored hat.

Im unteren Bereich ist eine Funktion, die ein Versprechen zurückgibt.

function timeout (ms) {
  return new Promise(function (resolve, reject) {
    setTimeout(function () {
      resolve("It was resolved!");
    }, ms)
  });
}

timeout(1000).then(function (dataFromPromise) {
  // logs "It was resolved!"
  console.log(dataFromPromise);
})

console.log("waiting...");

Konsolenausgabe

waiting...
// << pauses for one second>>
It was resolved!

Wenn das Timeout aufgerufen wird, wird die an den Promise-Konstruktor übergebene Funktion ohne Verzögerung ausgeführt. Dann wird die Methode setTimeout ausgeführt und der Callback wird in den nächsten ms Millisekunden ausgelöst, in diesem Fall ms=1000 . Da der Callback zum setTimeout noch nicht ausgelöst wird, gibt die Timeout-Funktion die Kontrolle an den aufrufenden Bereich zurück. Die Kette der then Methoden werden dann später genannt gespeichert werden , wenn / falls das Versprechen gelöst hat. Gäbe es catch hier Methoden würden sie auch gespeichert werden, sondern würden gefeuert werden , wenn / falls das Versprechen ‚ablehnt‘.

Das Skript druckt dann "Warten ...". Eine Sekunde später ruft der setTimeout seinen Callback auf, der die Auflösungsfunktion mit der Zeichenfolge "Es wurde aufgelöst!" Aufruft. Dieser String wird dann in der vergangen then Methode Callback und wird dann den Benutzer angemeldet.

In dem gleichen Sinn können Sie die asynchrone Funktion setTimeout umschließen, die einen Rückruf erfordert. Sie können jede einzelne asynchrone Aktion mit einem Versprechen umschließen.

Weitere Informationen zu Versprechungen finden Sie in der JavaScript-Dokumentation Versprechungen .



Modified text is an extract of the original Stack Overflow Documentation
Lizenziert unter CC BY-SA 3.0
Nicht angeschlossen an Stack Overflow