Heim  >  Artikel  >  Web-Frontend  >  Eine eingehende Analyse der Asynchronität in Node.js

Eine eingehende Analyse der Asynchronität in Node.js

青灯夜游
青灯夜游nach vorne
2021-06-08 10:39:472430Durchsuche

Dieser Artikel gibt Ihnen eine detaillierte Einführung in Asynchronität in Node.js. Es hat einen gewissen Referenzwert. Freunde in Not können sich darauf beziehen. Ich hoffe, es wird für alle hilfreich sein.

Eine eingehende Analyse der Asynchronität in Node.js

Über Node.js asynchron gibt es zwei Dinge, die nicht vermieden werden können: nicht blockierende E/A und Ereignisschleife. Gerade aufgrund dieser beiden Punkte kann Node.js als leistungsstark bezeichnet und in Online-Umgebungen eingesetzt werden. Lernen wir also den asynchronen Mechanismus und die Verwendung von Node.js kennen! [Empfohlenes Lernen: „nodejs-Tutorial“]

Die nicht blockierende E/A von Node.js

  • E/A ist Eingabe/Ausgabe, a Die Input/Output,一个系统的输入和输出
  • 阻塞 I/O 和非阻塞 I/O 的区别就在于系统接收输入再到输出期间,能不能接收其他输入

以点菜吃饭为例子:去饭堂点菜吃饭需要排队等待,在这个过程中,阿姨每次只能接待一个人,“点菜-阿姨抖勺装菜-把饭菜给到你”这个过程中阿姨并不能接收其他人的点菜,这个就是阻塞 I/O;而去餐馆点菜吃饭,去到餐馆就可以跟服务员你要吃番茄炒蛋,服务员记下来之后交给后厨,这时候来了另一桌人就把服务员招呼过去说想吃小龙虾,也就是说,在把菜给你上上来之前服务员接收了其他人的点菜,那这个就是非阻塞型 I/O。

理解非阻塞 I/O 的要点在于

  • 确定一个进行 Input/Output 的系统
  • 思考在 I/O 过程中,能不能进行其他 I/O

那在点菜吃饭这个例子中,一个进行 Input/Output 的系统就是点餐-后厨(阿姨)处理-上菜这样一个能让你吃上饭的系统;点餐就是 Input,上菜就是 Output,在这个例子中判断两者是非阻塞型还是阻塞型的关键就在于在点菜上菜这个过程中能不能接受其它的点菜上菜。就好比你点了个佛跳墙,等上菜可能就要好久了,然后来的人都是点一些简单的菜品,一分钟炒一份炒粉的那种,可能就是来来回回几波人之后都还没能给你上菜。

而 Node.js 它是用来操纵计算机的,一些如读取文件之类的操作是非常耗时的,要是不能进行其它的 I/O,那么处理效率就很会很低了,这也是 Node.js 是非阻塞型 I/O 的一个原因。

Node.js 的事件循环

Node.js 启动的时候会初始化由 libuv 提供的事件循环,每次的事件循环都包含6个阶段,这6个阶段会在每一次的事件循环当中按照下图当中的顺序反复执行,如下图:

Eine eingehende Analyse der Asynchronität in Node.js

  • timers 阶段:这个阶段执行 timersetTimeoutsetInterval)的回调
  • I/O callbacks 阶段 :处理一些上一轮循环中的少数未执行的 I/O 回调
  • idleprepare 阶段 :仅 Node 内部使用
  • poll 阶段 :获取新的 I/O 事件, 适当的条件下 Node 将阻塞在这里
  • check 阶段 :执行 setImmediate() 的回调
  • close callbacks 阶段:执行 socketclose 事件回调

每个阶段都有一个先入先出的(FIFO)的用于执行回调的队列,事件循环运行到每个阶段,都会从对应的回调队列中取出回调函数去执行,直到队列当中的内容耗尽,或者执行的回调数量达到了最大

然后事件循环就会进入下一个阶段,然后又从下一个阶段对应的队列中取出回调函数执行,这样反复直到事件循环的最后一个阶段。而事件循环也会一个一个按照循环执行,直到进程结束。

事件循环当中的6个宏队列和微队列的关系如下:微队列(microtask)在事件循环的各个阶段之间执行,或者说在事件循环的各个阶段对应的宏队列(macrotaskEin- und Ausgänge

des Systems.

Der Unterschied zwischen blockierender und nicht blockierender E/A besteht darin, ob das System während des Zeitraums von der Eingabe bis zur Ausgabe andere Eingaben empfangen kann. Eine eingehende Analyse der Asynchronität in Node.jsNehmen Sie das Bestellen von Essen als Beispiel: Um Essen in der Kantine zu bestellen, müssen Sie in der Schlange stehen. Während dieses Vorgangs kann die Tante jeweils nur eine Person empfangen: „Essen bestellen – Tante.“ schüttelt den Löffel, um das Essen zu laden – Während die Tante „dir das Essen bringt“, kann sie die Bestellungen anderer Leute nicht annehmen. Das merkt man, wenn man in ein Restaurant geht, um Essen zu bestellen dem Kellner, dass Sie Tomaten-Rührei essen möchten, wenn Sie ins Restaurant gehen. Nachdem Sie es aufgeschrieben haben, kommen Leute von einem anderen Tisch und sagen, dass sie Flusskrebse essen möchten . Mit anderen Worten, der Kellner

nahm die Bestellungen anderer Leute entgegen, bevor er Ihnen die Gerichte servierte. Dann ist dies eine nicht blockierende E/A.

Der Schlüssel zum Verständnis nicht blockierender E/A besteht darin, 🎜🎜🎜🎜ein System zu bestimmen🎜, das Eingabe/Ausgabe durchführt. 🎜🎜Überlegen Sie, ob während des E/A-Prozesses andere E/A-Vorgänge ausgeführt werden können🎜. 🎜Im Beispiel der Essensbestellung ist ein System, das Eingabe/Ausgabe ausführt, die Bestellung – verarbeitet durch die Küche (Tante) – das Servieren Ein System, mit dem Sie essen können, ist Eingabe und das Servieren ist Ausgabe. Bestimmen Sie in diesem Beispiel, ob die beiden nicht blockierend sind Sie können während des Bestell- und Serviervorgangs weitere Bestellungen und Gerichte annehmen. Es ist so, als würde man bei „Buddha springt über die Mauer“ lange warten, bis das Essen serviert wird. Dann bestellen die Leute, die kommen, ein paar einfache Gerichte, die Art von gebratenen Nudeln, die man darin frittieren kann Eine Minute. Vielleicht konnte ich Ihnen nach ein paar Wellen von Leuten noch kein Essen servieren. 🎜🎜Node.js wird zur Steuerung des Computers verwendet. Wenn andere E/A-Vorgänge nicht ausgeführt werden können, ist dies auch ein Grund dafür für nicht blockierende E/A. 🎜

🎜Node.js-Ereignisschleife🎜🎜🎜Node.js initialisiert die von libuv bereitgestellte 🎜Ereignisschleife🎜, wenn sie gestartet wird. Jede Ereignisschleife enthält 6 Stufen, diese 6 Stufen wird in jeder Ereignisschleife in der unten gezeigten Reihenfolge wiederholt ausgeführt: 🎜🎜Eine eingehende Analyse der Asynchronität in Node.js🎜🎜🎜timers-Stufe: Diese Stufe führt timer aus ( setTimeout, setInterval) Rückrufe🎜E/A-<code>Rückrufe-Stufe: Behandeln Sie einige nicht ausgeführte E/As im vorherigen Zyklus. Rückruf🎜idle, prepare Phase: wird nur intern vom Node verwendet🎜poll Phase: neues I/O-Ereignis abrufen, Node wird hier unter geeigneten Bedingungen blockieren🎜check-Phase: Führen Sie den Rückruf von setImmediate()🎜close callbacks-Phase aus: Führen Sie den <code>close-Ereignisrückruf aus of socket🎜🎜Jede Stufe verfügt über eine First-In-First-Out-Funktion (FIFO). In Bezug auf die Warteschlange zum Ausführen von Rückrufen gilt in jeder Stufe der Ereignisschleife die Die Rückruffunktion wird aus der entsprechenden Rückrufwarteschlange entnommen und ausgeführt, bis der Inhalt in der Warteschlange erschöpft ist oder die Anzahl der ausgeführten Rückrufe das Maximum erreicht. 🎜🎜Dann tritt die Ereignisschleife in die nächste Stufe ein, und dann wird die Rückruffunktion aus der Warteschlange entsprechend der nächsten Stufe entnommen und ausgeführt, und dies wird bis zur letzten Stufe der Ereignisschleife wiederholt. Die Ereignisschleife wird ebenfalls nacheinander ausgeführt, bis der Prozess beendet ist. 🎜🎜Die Beziehung zwischen den sechs Makrowarteschlangen und Mikrowarteschlangen in der Ereignisschleife ist wie folgt: Die Mikrowarteschlange (microtask) wird zwischen verschiedenen Phasen der Ereignisschleife oder den entsprechenden Makros in jeder Phase ausgeführt Die Ereignisschleife wird zwischen Warteschlangen ausgeführt (Makrotask). 🎜🎜🎜🎜🎜Hier ist eine besonders verwirrende Versionsänderung: 🎜
  • Wenn es Node10 und frühere Versionen ist: Es gibt mehrere Makroaufgaben in der Makrowarteschlange, und die Mikroaufgaben in der Mikrowarteschlange werden erst ausgeführt, wenn alle Makroaufgaben in der Makrowarteschlange abgeschlossen sind
  • Wenn es Node11 ist und spätere Version: Sobald eine Makroaufgabe (setTimeout, setInterval und setImmediate) in einer Phase ausgeführt wird, eine der drei, außer E/A ), führen Sie sofort die Mikrotask-Warteschlange aus, führen Sie alle Mikrotasks in der Mikrowarteschlange aus und kehren Sie dann gerade zur Makrowarteschlange zurück, um die nächste Makrotask auszuführen. Dies setTimeoutsetIntervalsetImmediate 三者其中之一,不包括I/O)就立刻执行微任务队列,执行完微队列当中的所有微任务再回到刚才的宏队列执行下一个宏任务。这就跟浏览器端运行一致了。

Node.js 异步编程 - callback

  • 回调函数格式规范
    • error-first callback
    • node-style callback
  • 第一个参数是 error,后面的参数才是结果。
// 第一个参数是错误捕获
interview(function (err, res) {
  if (err) {
    console.log(&#39;cry&#39;)
    return;
  }
  console.log(&#39;smile&#39;)
})
function interview(callback) {
  setTimeout(() => {
    if (Math.random() > 0.2) {
      callback(null, &#39;success&#39;)
    } else {
      callback(new Error(&#39;fail&#39;))
    }
  }, 500)
}

异步流程控制:回调地狱、异步并发等问题

  • npmasync.js;可以通过 async.js 来控制异步流程
  • thunk:一种编程方式

Node.js 异步编程 – Promise

  • 可以通过字面意思理解,Promise 是承诺的意思;当前事件循环得不到的结果,但未来的事件循环会给到你结果
  • 它是一个状态机,状态一旦确定为 resolvedrejected 就不会改变
    • pending:初始状态,还没得到结果的状态
    • fulfilled / resolved:成功状态
    • rejected:失败状态

链式调用:.then.catch

  • resolved 状态的 Promise 会回调后面的第一个 .then
  • rejected 状态的 Promise 会回调后面的第一个 .catch
  • 任何一个 rejected 状态且后面没有 .catchPromise,都会造成浏览器/ Node 环境的全局错误
// promise的状态转换以及通过then获取内容
const promise = new Promise((resolve, reject) => {
  setTimeout(function () {
    resolve(3);
    // reject(new Error(4))
  }, 500)
})

promise.then(function (result) {
  console.log(result)
}).catch(function (err) {
  console.log(err)
})

setTimeout(() => {
  console.log(promise)
}, 800)

执行 thencatch 会返回一个新 Promise,该 Promise 最终状态根据 thencatch 的回调函数的执行结果决定

  • 如果回调函数最终是 throw,该 Promiserejected 状态
  • 如果回调函数最终是 return,该 Promiseresolved 状态
  • 但如果回调函数最终 return 了一个 Promise ,该 Promise 会和回调函数 returnPromise 状态保持一致

Node.js 异步编程 – async/await

  • async functionPromise 的语法糖封装
  • 异步编程的终极方案 – 以同步的方式写异步
    • await 关键字可以“暂停” async function 的执行
    • await 关键字可以以同步的写法获取 Promise 的执行结果
    • try-catch 可以获取 await 所得到的错误
(async function () {
  await findJob()
  console.log(&#39;trip&#39;)
})()

async function findJob() {
  try {
    // 进行三轮面试
    await interview(1);
    await interview(2);
    await interview(3);
    console.log(&#39;smile&#39;)
  } catch (e) {
    console.log(&#39;cry at &#39; + e.round)
  }
}

// 进行第round轮面试
function interview(round) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      if (Math.random() < 0.2) {
        const error = new Error(&#39;failed&#39;);
        error.round = round;
        reject(error);
      } else {
        resolve(&#39;success&#39;);
      }
    }, 500)
  })
}

这是一个穿越事件循环存在的 function

总结

  • 理解非阻塞 I/O 主要在于确定一个进行 I/O 的系统,然后思考判断能不能进行其它 I/O
  • Node.js 的事件循环在 Node11 版本及之后是和浏览器的事件循环运行一致的,要注意区分。
  • Node.js 异步编程的规范是第一个参数是 error,后面的才是结果。
  • Promise是一个状态机,初始状态为 pending,一旦确定状态为 resolvedrejected 就不会改变,可以通过 .then.catch 进行链式调用。
  • async/await stimmt mit der browserseitigen Bedienung überein.
Node.js asynchrone Programmierung – callback

Callback-Funktionsformatspezifikation🎜error-first callback🎜🎜<code>node-style callback🎜🎜🎜🎜Der erste Parameter ist error, und die folgenden Parameter sind das Ergebnis. 🎜🎜rrreee🎜Asynchrone Prozesssteuerung: Callback-Hölle, asynchrone Parallelität und andere Probleme🎜🎜🎜npm: async.js kann an async.js übergeben werden; code> Zur Steuerung asynchroner Prozesse🎜🎜<code>thunk: eine Programmiermethode🎜🎜

🎜Node.js asynchrone Programmierung – Versprechen🎜 🎜🎜 kann wörtlich verstanden werden. Promise bedeutet, dass die aktuelle Ereignisschleife das Ergebnis nicht erhalten kann, aber die zukünftige Ereignisschleife wird Ihnen das Ergebnis liefern. Es handelt sich einmal um eine Zustandsmaschine Der Status wird als aufgelöst oder abgelehnt bestimmt, er ändert sich nicht🎜🎜ausstehend: Anfangsstatus, der Status, in dem das Ergebnis noch nicht vorhanden war noch erhalten🎜🎜erfüllt / gelöst: Erfolgsstatus 🎜🎜abgelehnt: Fehlerstatus 🎜🎜🎜🎜🎜Kettenaufruf: .then und der .catch🎜🎜🎜resolved Status Promise rufen das erste .then🎜 zurück 🎜Promise im Status >abgelehnt

ruft den ersten .catch zurück. 🎜🎜Jeder abgelehnte-Status, der dies nicht tut Wenn Sie dem Promise von .catch folgen, wird ein globaler Fehler in der Browser-/Knotenumgebung verursacht. 🎜🎜rrreee🎜Das Ausführen von then und catch will Gibt ein neues Promise zurück, dessen endgültiger Zustand basierend auf den Ausführungsergebnissen der Rückruffunktionen von then und catch bestimmt wird 🎜🎜 🎜Wenn die Callback-Funktion am Ende throw ist, befindet sich das Promise im Status rejected. 🎜🎜Wenn die Callback-Funktion am Ende ist return, Das Promise befindet sich im Status aufgelöst 🎜🎜Aber wenn die Rückruffunktion schließlich zurückgibt ein Promise code>, das <code>Promise stimmt mit dem Promise-Status der Rückruffunktion return überein 🎜🎜

🎜Node.js asynchrone Programmierung – async

/await🎜🎜🎜async function ist ein syntaktisches Zuckerpaket von Versprechen🎜🎜für asynchrone Programmierung Die ultimative Lösung – asynchron auf synchrone Weise schreiben🎜🎜 Das Schlüsselwort await kann die Ausführung der asynchronen Funktion „anhalten“🎜🎜 Das Schlüsselwort await kann verwendet werden. Die synchrone Schreibmethode erhält das Ausführungsergebnis von Promise🎜🎜try-catch kann den von await erhaltenen Fehler abrufen 🎜🎜🎜🎜rrreee🎜Dies ist eine A-Funktion, die über die Ereignisschleife existiert. 🎜

🎜Zusammenfassung🎜

🎜🎜Das Verständnis nicht blockierender E/A liegt hauptsächlich darin, 🎜ein System zu bestimmen, das E/A ausführt, und dann darüber nachzudenken, ob andere E/A O kann durchgeführt werden 🎜. 🎜🎜Die 🎜Ereignisschleife🎜 von Node.js läuft genauso ab wie die Ereignisschleife des Browsers in der Node11-Version und höher, achten Sie also bitte auf die Unterscheidung. 🎜🎜Die Spezifikation der asynchronen Programmierung von Node.js besteht darin, dass der erste Parameter error ist und das Folgende das Ergebnis ist. 🎜🎜Promise ist eine 🎜Zustandsmaschine🎜 Der Anfangszustand ist ausstehend. Sobald der Zustand bestimmt ist, ist er aufgelöst abgelehnt Es ändert sich nicht und Kettenaufrufe können über .then und .catch durchgeführt werden. 🎜🎜async/await 🎜Asynchrones Schreiben auf synchrone Weise🎜 ist die ultimative Lösung für die asynchrone Programmierung. 🎜🎜🎜Weitere Programmierkenntnisse finden Sie unter: 🎜Programmiervideo🎜! ! 🎜

Das obige ist der detaillierte Inhalt vonEine eingehende Analyse der Asynchronität in Node.js. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Stellungnahme:
Dieser Artikel ist reproduziert unter:juejin.cn. Bei Verstößen wenden Sie sich bitte an admin@php.cn löschen