
Asynchron dient dazu, die CPU-Auslastung zu erhöhen und sie ständig beschäftigt zu halten.
Einige Vorgänge (der typischste ist E/A) erfordern keine CPU-Beteiligung und sind sehr zeitaufwändig. Wenn Asynchronität nicht verwendet wird, entsteht ein Blockierungszustand, der dazu führt, dass die CPU im Leerlauf ist und die Seite einfriert.
Wenn eine E/A-Operation in einer asynchronen Umgebung auftritt, legt die CPU die E/A-Arbeit beiseite (zu diesem Zeitpunkt wird die E/A von anderen Controllern übernommen und es werden weiterhin Daten übertragen) und verarbeitet dann die nächste Aufgabe Warten Sie, bis der E/A-Vorgang abgeschlossen ist. Benachrichtigen Sie die CPU (Rückruf ist eine Benachrichtigungsmethode), damit sie wieder funktioniert.
Der Kerninhalt von „JavaScript Asynchronous and Callback“ besteht darin, dass die spezifische Endzeit der asynchronen Arbeit ungewiss ist. Um die nachfolgende Verarbeitung nach Abschluss der asynchronen Arbeit genau durchzuführen, muss ein Rückruf an die asynchrone Funktion übergeben werden Nachdem Sie Ihre Arbeit abgeschlossen haben, fahren Sie mit den folgenden Aufgaben fort.
Obwohl Callbacks sehr einfach asynchron zu implementieren sein können, können sie aufgrund der mehrfachen Verschachtelung eine Callback-Hölle bilden. Um die Callback-Hölle zu vermeiden, müssen Sie die verschachtelte Programmierung entschachteln und in eine lineare Programmierung ändern.
Promise ist die beste Lösung, um die Callback-Hölle in JavaScript zu bewältigen.
Promise kann als „Versprechen“ übersetzt werden. Wir können asynchrone Arbeit kapseln und sie „ Promise “ nennen, das heißt, ein Versprechen abgeben und versprechen, nach dem Ende der asynchronen Arbeit ein klares Signal zu geben!
Promise Syntax:
let Promise = new Promise(function(resolve,reject){
// Asynchrone Arbeit}) Durch die obige Syntax können wir asynchrone Arbeit in ein Promise kapseln. Die beim Erstellen Promise übergebene Funktion ist die Methode zur Verarbeitung asynchroner Arbeit, auch executor genannt.
resolve und reject sind Rückruffunktionen, die von JavaScript selbst bereitgestellt werden. Sie können aufgerufen werden, wenn executor die Aufgabe abschließt:
resolve(result) – wenn sie erfolgreich abgeschlossen wird, wird result „error reject(error) zurückgegeben – wenn die Ausführung fehlschlägt Es wird error generiert.executor wird automatisch ausgeführt, sobald Promise erstellt wurde, und sein Ausführungsstatus ändert den Status der internen Eigenschaften Promise :
state – zunächst pending , dann nach dem Aufruf von resolve in fulfilled “ umgewandelt oder wird rejected wenn reject aufgerufen wird;result – Es undefined und wird dann zum value , nachdem resolve(value) aufgerufen wurde, oder wird zu error , nachdem reject aufgerufen wurde.fs.readFile eine asynchrone Funktion Wir können es an executor übergeben, um Dateilesevorgänge in der Datei durchzuführen, wodurch asynchrone Arbeit gekapselt wird.
Der folgende Code kapselt die Funktion fs.readFile und verwendet resolve(data) für die Verarbeitung erfolgreicher Ergebnisse und reject(err) für die Verarbeitung fehlgeschlagener Ergebnisse.
Der Code lautet wie folgt:
let Promise = new Promise((resolve,ject) => {
fs.readFile('1.txt', (err, data) => {
console.log('Read 1.txt')
wenn (fehler) ablehnen (fehler)
auflösen(Daten)
})}) Wenn wir diesen Code ausführen, werden die Worte „Read 1.txt“ ausgegeben, was beweist, dass der Dateilesevorgang unmittelbar nach der Erstellung Promise ausgeführt wird.
Promisekapselt normalerweise asynchronen Code intern, es kapselt jedoch nicht nur asynchronen Code.
Der obige Promise -Fall kapselt den Dateilesevorgang. Die Datei wird unmittelbar nach Abschluss der Erstellung gelesen. Wenn Sie das Ergebnis der Promise -Ausführung erhalten möchten, müssen Sie drei Methoden verwenden then , catch und finally .
Die then Methode von Promise kann verwendet werden, um die Arbeit zu erledigen, nachdem Promise Ausführung abgeschlossen ist. Sie erhält zwei Rückrufparameter. Die Syntax lautet wie folgt:
Promise.then(function(result),function(error))
result ist der von resolve empfangene Wert;error ist der von rejectempfangene
Parameter Versprechen = neues Versprechen((lösen, ablehnen) => {
fs.readFile('1.txt', (err, data) => {
console.log('Read 1.txt')
wenn (fehler) ablehnen (fehler)
auflösen(Daten)
})})promise.then(
(Daten) => {
console.log('Erfolgreich ausgeführt, das Ergebnis ist' + data.toString())
},
(irrt) => {
console.log('Ausführung fehlgeschlagen, Fehler ist' + err.message)
}) Wenn das Lesen der Datei erfolgreich ausgeführt wurde, wird die erste Funktion aufgerufen:
PS E:CodeNodedemos 3-callback> node .index.js Lesen Sie 1.txt Bei erfolgreicher Ausführung ist das Ergebnis 1
gelöscht 1.txt . Wenn die Ausführung fehlschlägt, wird die zweite Funktion aufgerufen:
PS E:CodeNodedemos 3-callback> node .index.js Lesen Sie 1.txt Die Ausführung ist mit dem Fehler ENOENT: no such file or directory, open 'E:CodeNodedemos 3-callback1.txt' fehlgeschlagen.
Wenn wir uns nur auf das Ergebnis einer erfolgreichen Ausführung konzentrieren, können wir nur eines übergeben Rückruffunktion:
Promise .then((data)=>{
console.log('Erfolgreich ausgeführt, das Ergebnis ist' + data.toString())}) An diesem Punkt haben wir einen asynchronen Lesevorgang der Datei implementiert.
Wenn wir uns nur auf das Fehlerergebnis konzentrieren, können wir null an den ersten then übergeben: promise.then(null,(err)=>{...}) .
Oder verwenden Sie einen eleganteren Weg: promise.catch((err)=>{...})
let Promise = new Promise((resolve,ject) => {
fs.readFile('1.txt', (err, data) => {
console.log('Read 1.txt')
wenn (fehler) ablehnen (fehler)
auflösen(Daten)
})})promise.catch((err)=>{
console.log(err.message)}) .catch((err)=>{...}) und then(null,(err)=>{...}) haben genau den gleichen Effekt.
.finally ist eine Funktion, die unabhängig vom Ergebnis von promise ausgeführt wird. Sie hat den gleichen Zweck wie finally in try...catch... -Syntax und kann Vorgänge verarbeiten, die nichts mit dem Ergebnis zu tun haben.
Zum Beispiel:
new Promise((resolve,reject)=>{
//etwas...}).finally(()=>{console.log('Unabhängig vom Ergebnis ausführen')}).then(result=>{...}, err=>{...} ) Der „final“-Rückruf hat keine Parameter fs.readFile() liest 10 Dateien nacheinander und gibt den Inhalt aus der zehn Dateien nacheinander.
Da fs.readFile() selbst asynchron ist, müssen wir die Rückrufverschachtelung verwenden. Der Code lautet wie folgt:
fs.readFile('1.txt', (err, data) => {
console.log(data.toString()) //1
fs.readFile('2.txt', (err, data) => {
console.log(data.toString())
fs.readFile('3.txt', (err, data) => {
console.log(data.toString())
fs.readFile('4.txt', (err, data) => {
console.log(data.toString())
fs.readFile('5.txt', (err, data) => {
console.log(data.toString())
fs.readFile('6.txt', (err, data) => {
console.log(data.toString())
fs.readFile('7.txt', (err, data) => {
console.log(data.toString())
fs.readFile('8.txt', (err, data) => {
console.log(data.toString())
fs.readFile('9.txt', (err, data) => {
console.log(data.toString())
fs.readFile('10.txt', (err, data) => {
console.log(data.toString())
// ==> Hell's Gate})
})
})
})
})
})
})
})
})}) Obwohl der obige Code die Aufgabe erledigen kann, wird die Codeebene mit zunehmender Aufrufverschachtelung tiefer und die Wartungsschwierigkeit nimmt zu, insbesondere wenn wir echten Code verwenden, der möglicherweise viele Schleifen und bedingte Anweisungen enthält einfaches console.log(...) im Beispiel.
Was passiert, wenn wir keine Rückrufe verwenden und fs.readFile() direkt nacheinander gemäß dem folgenden Code aufrufen?
//Hinweis: Dies ist die falsche Schreibweise fs.readFile('1.txt', (err, data) => {
console.log(data.toString())})fs.readFile('2.txt', (err, data) => {
console.log(data.toString())})fs.readFile('3.txt', (err, data) => {
console.log(data.toString())})fs.readFile('4.txt', (err, data) => {
console.log(data.toString())})fs.readFile('5.txt', (err, data) => {
console.log(data.toString())})fs.readFile('6.txt', (err, data) => {
console.log(data.toString())})fs.readFile('7.txt', (err, data) => {
console.log(data.toString())})fs.readFile('8.txt', (err, data) => {
console.log(data.toString())})fs.readFile('9.txt', (err, data) => {
console.log(data.toString())})fs.readFile('10.txt', (err, data) => {
console.log(data.toString())}) Das Folgende sind die Ergebnisse meines Tests (die Ergebnisse jeder Ausführung sind unterschiedlich):
PS E:CodeNodedemos 3-callback> node .index DerDer Grund dafür, dass
js12346957108
dieses nicht-sequentielle Ergebnis erzeugt, ist asynchron . Es kann keine Multithread-Parallelität erreicht werden. Asynchronität kann in einem einzelnen Thread erreicht werden.
Der Grund, warum dieser Fehlerfall hier verwendet wird, besteht darin, das Konzept der Asynchronität hervorzuheben. Wenn Sie nicht verstehen, warum dieses Ergebnis auftritt, müssen Sie zurückgehen und die Lektion nachholen!
Die Idee, Promise zum Lösen des asynchronen sequentiellen Dateilesens zu verwenden:
promise1 zu lesen, und verwenden Sie resolve , um das Ergebnis zurückzugeben.promise2 ,promise1.then das Ergebnis des Dateilesens zu empfangen und auszugebenPromise1.then
promise1.then Und zurück,promise2.then aufzurufen, um das Leseergebnis zu empfangen und auszugebenpromise3 Objekt in promise2.then und kehren Sie zurück, umpromise3.then aufzurufen, um das Leseergebnis zu empfangen undDer Code lautet wie folgt:
let Promise1 = new Promise( (auflösen, ablehnen) => {
fs.readFile('1.txt', (err, data) => {
wenn (fehler) ablehnen (fehler)
auflösen(Daten)
})})let Promise2 = Promise1.then(
Daten => {
console.log(data.toString())
neues Versprechen zurückgeben((auflösen, ablehnen) => {
fs.readFile('2.txt', (err, data) => {
wenn (fehler) ablehnen (fehler)
auflösen(Daten)
})
})
})lass versprechen3 = versprechen2.then(
Daten => {
console.log(data.toString())
neues Versprechen zurückgeben((auflösen, ablehnen) => {
fs.readFile('3.txt', (err, data) => {
wenn (fehler) ablehnen (fehler)
auflösen(Daten)
})
})
})lass versprechen4 = versprechen3.then(
Daten => {
console.log(data.toString())
//.....
})... ... Auf diese Weise schreiben wir die ursprüngliche verschachtelte Callback-Hölle in einen linearen Modus.
Es gibt jedoch immer noch ein Problem mit dem Code. Obwohl der Code in Bezug auf die Verwaltung schöner geworden ist, nimmt die Länge des Codes erheblich zu.
Der obige Code ist zu lang. Wir können die Codemenge durch zwei Schritte reduzieren:
promise weglassen und die .then Datei verknüpfenCode wie folgt:
function myReadFile (path) {
neues Versprechen zurückgeben((auflösen, ablehnen) => {
fs.readFile(path, (err, data) => {
wenn (fehler) ablehnen (fehler)
console.log(data.toString())
lösen()
})
})}myReadFile('1.txt')
.then(data => { return myReadFile('2.txt') })
.then(data => { return myReadFile('3.txt') })
.then(data => { return myReadFile('4.txt') })
.then(data => { return myReadFile('5.txt') })
.then(data => { return myReadFile('6.txt') })
.then(data => { return myReadFile('7.txt') })
.then(data => { return myReadFile('8.txt') })
.then(data => { return myReadFile('9.txt') })
.then(data => { return myReadFile('10.txt') }) Da die myReadFile -Methode ein neues Promise zurückgibt, können wir die .then -Methode direkt ausführen. Diese Programmiermethode wird Kettenprogrammierung genannt.
Das Ergebnis der Codeausführung lautet wie folgt:
PS E:CodeNodedemos 3-callback> node .index.js12345678910
Damit ist der asynchrone und sequentielle Dateilesevorgang abgeschlossen.
Hinweis: In der
.thenMethode jedes Schritts muss ein neuesPromiseObjekt zurückgegeben werden, andernfalls wird das vorherige altePromiseempfangen.Dies liegt daran, dass jede
then-Methode ihrPromiseweiterhin nach unten weitergibt.