Heim > Artikel > Web-Frontend > Zusammenfassung und Weitergabe, um mehrere Schlüsselknoten von NodeJS zu verstehen
Dieser Artikel ist ein persönliches Verständnis von nodejs in der tatsächlichen Entwicklung und beim Lernen. Er ist jetzt zum späteren Nachschlagen zusammengestellt. Es wäre mir eine Ehre, wenn er Sie inspirieren könnte.
E/A: Eingabe/Ausgabe, die Eingabe und Ausgabe eines Systems.
Ein System kann als Individuum verstanden werden, beispielsweise als eine Person. Wenn Sie sprechen, ist es der Output, und wenn Sie zuhören, ist es der Input.
Der Unterschied zwischen blockierender und nicht blockierender E/A besteht darin, ob das System im Zeitraum von der Eingabe bis zur Ausgabe andere Eingaben empfangen kann. Das Folgende sind zwei Beispiele, um zu veranschaulichen, was blockierende I/O und nicht-blockierende I/O sind:
1 Mahlzeiten zubereiten
Zuerst müssen wir den Umfang eines Systems bestimmen. In diesem Beispiel stellt sich die Cafeteria-Tante ein System vor, bei dem der Kellner im Restaurant bestellt und das Essen serviert.
Wenn Sie dann zwischen der Bestellung und dem Servieren von Speisen die Bestellungen anderer Personen annehmen können, können Sie feststellen, ob es sich um blockierende oder nicht blockierende E/A handelt. Für die Cafeteria-Tante kann sie beim Bestellen nicht für andere Schüler bestellen. Erst nachdem der Student die Bestellung abgeschlossen und das Geschirr serviert hat, kann er die Bestellung des nächsten Schülers annehmen, sodass die Cafeteria-Tante den I/O sperrt.
Für den Restaurantkellner kann er den nächsten Gast nach der Bestellung und bevor der Gast das Gericht serviert, bedienen, sodass der Kellner über nicht blockierende E/A verfügt.
2. Hausarbeit erledigenZu diesem Zeitpunkt müssen Sie nicht auf die Waschmaschine warten, sondern können den Schreibtisch und die Kleidung aufräumen Zu diesem Zeitpunkt wird die Wäsche aufgehängt, dann dauert es insgesamt nur 25 Minuten.
Wäsche ist eigentlich ein nicht blockierender I/O. Zwischen dem Einlegen der Kleidung in die Waschmaschine und dem Beenden des Waschvorgangs können Sie andere Dinge tun.
Der Grund, warum nicht blockierende E/A die Leistung verbessern kann, besteht darin, dass unnötiges Warten eingespart werden kann.Der Schlüssel zum Verständnis nicht blockierender E/A ist :
Bestimmen Sie eine Systemgrenze
für E/A. Dies ist sehr kritisch, wenn das System wie im obigen Restaurantbeispiel erweitert wird, wenn das System auf das gesamte Restaurant ausgeweitet wird, dann wird der Koch definitiv ein blockierender I/O sein.Wenn das Architekturdiagramm unten nach Thread-Wartung unterteilt ist, ist die gepunktete Linie links der NodeJS-Thread und die gepunktete Linie rechts der C++-Thread.
Jetzt muss der Nodejs-Thread die Datenbank abfragen. Dies ist ein typischer E/A-Vorgang. Er wird nicht auf die Ergebnisse des E/A-Vorgangs warten und eine große Menge an Daten verteilen Rechenleistung für andere zu berechnende C++-Threads. Warten Sie, bis das Ergebnis ausgegeben wird, und geben Sie es an den NodeJS-Thread zurück. Bevor Sie das Ergebnis erhalten, kann der NodeJS-Thread auch andere E/A-Vorgänge ausführen, sodass er nicht blockiert. nodejs-Threadentspricht dem linken Teil als Kellner und dem C++-Thread als Chef.
Die nicht blockierende E/A des Knotens wird also durch den Aufruf von C++-Worker-Threads abgeschlossen.Wie benachrichtige ich den NodeJS-Thread, wenn der C++-Thread das Ergebnis erhält? Die Antwort ist
ereignisgesteuert.
EreignisgesteuertBlockierung
: Die Funktion kehrt während der I/O sofort zurück /O, und der Prozess wartet nicht auf den E/A-Abschluss. Um dann das zurückgegebene Ergebnis zu erfahren, müssen Sie den
Ereignistreiberverwenden. Das sogenannte
Ereignisgesteuertekann als das gleiche verstanden werden wie das Front-End-Klickereignis. Ich schreibe zuerst ein Klickereignis, weiß aber nicht, wann es ausgelöst wird. Der Hauptthread führt die ereignisgesteuerte Funktion aus. Dieser Modus ist auch ein Beobachtermodus, das heißt, ich höre mir zuerst das Ereignis an und führe es dann aus, wenn es ausgelöst wird.
Wie implementiert man also Event Drive? Die Antwort ist Asynchrone Programmierung.
Asynchrone ProgrammierungWie oben erwähnt, verfügt NodeJS über eine große Anzahl nicht blockierender E/A, daher müssen die Ergebnisse nicht blockierender E/A über Rückruffunktionen abgerufen werden.
Diese Methode verwendet Rückruffunktionen ist asynchrone Programmierungglob(__dirname+'/**/*', (err, res) => { result = res console.log('get result') })
Der erste Parameter der NodeJS-Callback-Funktion ist Fehler, und die nachfolgenden Parameter sind das Ergebnis. Warum das tun?
try { interview(function () { console.log('smile') }) } catch(err) { console.log('cry', err) } function interview(callback) { setTimeout(() => { if(Math.random() <p>Nach der Ausführung wurde es nicht abgefangen und der Fehler wurde global ausgelöst, was zum Absturz des gesamten NodeJS-Programms führte. </p><p><img src="https://img.php.cn/upload/image/244/886/980/1657110712466688.png" title="1657110712466688.png" alt="Zusammenfassung und Weitergabe, um mehrere Schlüsselknoten von NodeJS zu verstehen"></p><p> wird von try Catch nicht erfasst, da setTimeout die Ereignisschleife erneut öffnet. Jedes Mal, wenn eine Ereignisschleife geöffnet wird, wird ein Aufrufstapelkontext neu generiert, der zum Aufrufstapel der vorherigen Ereignisschleife und der Rückruffunktion gehört Wenn setTimeout ausgeführt wird, ist der Aufrufstapel anders. In diesem neuen Aufrufstapel gibt es keinen Try-Catch, daher wird der Fehler global ausgelöst und kann nicht abgefangen werden. Weitere Informationen finden Sie in diesem Artikel. Probleme bei der Verwendung asynchroner Warteschlangen für Try Catch. <a href="https://juejin.cn/post/6995749646366670855" target="_blank" title="https://juejin.cn/post/6995749646366670855"></a>Was sollen wir also tun? Verwenden Sie den Fehler als Parameter: </p><pre class="brush:php;toolbar:false">function interview(callback) { setTimeout(() => { if(Math.random() <p> Dies ist jedoch problematischer und muss im Rückruf beurteilt werden, sodass eine ausgereifte Konvention erstellt wird. Der erste Parameter ist err erfolgreich. </p><pre class="brush:php;toolbar:false">function interview(callback) { setTimeout(() => { if(Math.random() <p></p>Asynchrone Prozesssteuerung<h3 data-id="heading-5"> <strong></strong>Die Callback-Schreibmethode von NodeJS bringt nicht nur Callback-Bereiche mit sich, sondern auch Probleme mit der asynchronen Prozesssteuerung. </h3><p>Asynchrone Prozesssteuerung bezieht sich hauptsächlich auf die Handhabung der Parallelitätslogik, wenn Parallelität auftritt. Nehmen wir weiterhin das obige Beispiel: Wenn Ihr Kollege zwei Unternehmen interviewt, wird er erst dann von dem dritten Unternehmen interviewt, wenn er erfolgreich zwei Unternehmen interviewt hat. Wie schreibt man diese Logik? Es ist notwendig, jede Variable count:<strong><pre class="brush:php;toolbar:false">var count = 0 interview((err) => { if (err) { return } count++ if (count >= 2) { // 处理逻辑 } }) interview((err) => { if (err) { return } count++ if (count >= 2) { // 处理逻辑 } })wie die obige Methode zu unterstützen, was sehr mühsam und hässlich ist. Daher erschienen später die Schreibmethoden Promise und Async/Await.
Versprechen ist nicht nur ein Drecksack, sondern auch eine Zustandsmaschine:ausstehenderfüllt/gelöst
const pro = new Promise((resolve, reject) => { setTimeout(() => { resolve('2') }, 200) }) console.log(pro) // 打印:Promise { <pending> }</pending>
ein neues Versprechen zurückgeben Der Endzustand des Versprechens wird durch die Ausführungsergebnisse der Callback-Funktionen von then und Catch bestimmt:
Wenn die Callback-Funktion immer throw new ist Fehler, das Versprechen ist im Status „Abgelehnt“
Wenn die Rückruffunktion immer zurückkehrt, befindet sich das Versprechen im gelösten Zustandfunction interview() { return new Promise((resolve, reject) => { setTimeout(() => { if (Math.random() > 0.5) { resolve('success') } else { reject(new Error('fail')) } }) }) } var promise = interview() var promise1 = promise.then(() => { return new Promise((resolve, reject) => { setTimeout(() => { resolve('accept') }, 400) }) })Der Status von Versprechen1 wird durch den Status des Gegenzugsversprechens bestimmt, d. h. durch den Status von Versprechen1, nachdem das Gegenzugsversprechen ausgeführt wurde. Was sind die Vorteile davon? Dies kann „das Problem der Rückrufhölle lösen“.
var promise = interview() .then(() => { return interview() }) .then(() => { return interview() }) .then(() => { return interview() }) .catch(e => { console.log(e) })
Versprechen löst asynchrone Prozesssteuerung
Wenn Versprechen nur dazu dienen, Höllenrückrufe zu lösen, ist es zu klein, um Versprechen zu unterschätzen. Die Hauptfunktion von Versprechen besteht darin, asynchrone Prozesssteuerungsprobleme zu lösen. Wenn Sie zwei Unternehmen gleichzeitig interviewen möchten:
function interview() { return new Promise((resolve, reject) => { setTimeout(() => { if (Math.random() > 0.5) { resolve('success') } else { reject(new Error('fail')) } }) }) } promise .all([interview(), interview()]) .then(() => { console.log('smile') }) // 如果有一家公司rejected,就catch .catch(() => { console.log('cry') })
sync/await Was genau ist:
console.log(async function() { return 4 }) console.log(function() { return new Promise((resolve, reject) => { resolve(4) }) })
try { await interview(1) await interview(2) await interview(2) } catch(e => { console.log(e) })Was ist, wenn es eine parallele Aufgabe ist?
await Promise.all([interview(1), interview(2)])EreignisschleifeAufgrund der nicht blockierenden E/A von NodeJS müssen Sie einen ereignisgesteuerten Ansatz verwenden, um die E/A-Ergebnisse zu erhalten. Um ereignisgesteuerte Ergebnisse zu erzielen, müssen Sie asynchrone Programmierung verwenden , wie z. B. Rückruffunktionen. Wie führt man diese Rückruffunktionen aus, um die Ergebnisse zu erhalten? Dann müssen Sie eine Ereignisschleife verwenden. Die Ereignisschleife ist die wichtigste Grundlage für die Realisierung der nicht blockierenden E/A-Funktion von nodejs. Nicht blockierende E/A und Ereignisschleife sind beides Funktionen, die von
代码演示:
const eventloop = { queue: [], loop() { while(this.queue.length) { const callback = this.queue.shift() callback() } setTimeout(this.loop.bind(this), 50) }, add(callback) { this.queue.push(callback) } } eventloop.loop() setTimeout(() => { eventloop.add(() => { console.log('1') }) }, 500) setTimeout(() => { eventloop.add(() => { console.log('2') }) }, 800)
setTimeout(this.loop.bind(this), 50)
保证了50ms就会去看队列中是否有回调,如果有就去执行。这样就形成了一个事件循环。
当然实际的事件要复杂的多,队列也不止一个,比如有一个文件操作对列,一个时间对列。
const eventloop = { queue: [], fsQueue: [], timerQueue: [], loop() { while(this.queue.length) { const callback = this.queue.shift() callback() } this.fsQueue.forEach(callback => { if (done) { callback() } }) setTimeout(this.loop.bind(this), 50) }, add(callback) { this.queue.push(callback) } }
首先我们弄清楚了什么是非阻塞I/O,即遇到I/O立刻跳过执行后面的任务,不会等待I/O的结果。当I/O处理好了之后就会调用我们注册的事件处理函数,这就叫事件驱动。实现事件驱动就必须要用异步编程,异步编程是nodejs中最重要的环节,它从回调函数到promise,最后到async/await(使用同步的方法写异步逻辑)。
更多node相关知识,请访问:nodejs 教程!
Das obige ist der detaillierte Inhalt vonZusammenfassung und Weitergabe, um mehrere Schlüsselknoten von NodeJS zu verstehen. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!