Heim > Web-Frontend > js-Tutorial > Hauptteil

Lassen Sie uns über Modularität und Ereignisschleife in Nodejs sprechen

青灯夜游
Freigeben: 2021-07-01 19:08:43
nach vorne
2062 Leute haben es durchsucht

Dieser Artikel führt Sie durch die Modularisierungs- und Ereignisschleife in Nodejs. Es hat einen gewissen Referenzwert. Freunde in Not können sich darauf beziehen. Ich hoffe, es wird für alle hilfreich sein.

Lassen Sie uns über Modularität und Ereignisschleife in Nodejs sprechen

5.20 hat eine Online-Idee veröffentlicht, die Node.js im Browser ausführen kann – WebContainers

1 Einführung in Node.js

Was genau ist Node.js? Als ich anfing zu lernen, hatte ich nicht viel Kontakt zu einigen Front-End-Wissensbereichen (natürlich habe ich heute noch den gleichen Eindruck von Node.js, dass seine Syntax fast der von Javascript entspricht, und dann). Schreiben Sie das Backend. Ich erinnere mich, dass ich damals insgeheim glücklich war, Javascript zu lernen = alles zu wissen! Okay, kommen wir zum Punkt

[Empfohlene Studie: „nodejs-Tutorial“]

In der Vergangenheit wurde Javascript im Browser ausgeführt, schließlich ist es eine Hochsprache, die nicht direkt gelesen werden kann In der Welt der Binärcomputer gibt es nur 010101 ... Zu diesem Zeitpunkt fungiert die JavaScript-Engine im Browser als Übersetzer und übersetzt Schritt für Schritt, was JavaScript auf dem Computer tun möchte. Der Kompilierungsprozess wird nicht detailliert beschrieben (. Ich werde auch vorerst sagen: Nicht sicher).

Node.js basiert auf der V8-Engine des Chrome-Browsers und kann Javascript effizient kompilieren. Man kann also sagen, dass Node.js neben dem Browser eine weitere Javascript-Ausführungsumgebung ist.

Ich erinnere mich, dass ich mit einfachen automatischen Antworten für öffentliche WeChat-Konten in den Cloud-Funktionen von Tencent Cloud herumgespielt habe. Damals hatte ich dank Node.js ein wenig Erfahrung mit der Modularisierung von Front-End-Code!

2 Erste Erfahrung

server.js-Datei ist wie folgt

// 引入 http 模块
var http = require("http");

//  用 http 模块创建服务
 //req 获取 url 信息 (request)
 //res 浏览器返回响应信息 (response)
http.createServer(function (req, res) {
  // 设置 HTTP 头部,状态码是 200,文件类型是 html,字符集是 utf8
  //Content-Type字段用于定义网络文件的类型和网页的编码,决定浏览器将以什么形式、什么编码读取这个文件,不写就可能会出现乱码哦
  res.writeHead(200, {
    "Content-Type": "text/html;charset=UTF-8"
  });

  // 往页面打印值
  res.write('小林别闹');

  // 结束响应
  res.end();

}).listen(3000); // 监听3000端口
Nach dem Login kopieren

Führen Sie node server.js im Terminal aus, nachdem Node installiert wurde, und geben Sie http ein Adressleiste: //localhost:3000/ Sie können die gedruckte Seite sehen:

node server.js 打开浏览器,在地址栏输入http://localhost:3000/ 就能看到页面打印出来:

Lassen Sie uns über Modularität und Ereignisschleife in Nodejs sprechen

此时我们在本地搭建起一个最简单的服务器,浏览器作为客户端进行访问

2.1 模块化

在上边的代码中,我们注意到了有 var http = require("http"); 这样的语句,作用是引入 http 模块. 在 Node 中,模块分为两类:一是 Node 提供的模块,称为核心模块;二是用户编写的模块,称为文件模块.http 就是核心模块之一,例如使用 http 模块可以创建服务,path 模块处理文件路径,url 模块用于处理与解析 URL.fs模块用于对系统文件及目录进行读写操作等.

2.1.1CommonJS

提到模块化,就必须提一嘴 CommonJS,Node.js 就采用了部分 CommonJS 语法,可以理解为 CommonJS 是一种模块化的标准.在早期为了解决通过script标签引入js文件代码产生的依赖顺序易出错,顶层作用域导致的变量污染等问题

在这里可以梳理一下导出 module.exportsexports 的差别

test2.js 如下:

let str = require('./test1');
console.log(str)
Nach dem Login kopieren

当 test1.js如下:

let str1 = '小林别闹1'
let str2 = '小林别闹2'

exports.str1 = str1
exports.str2 = str2
console.log(module.exports === exports)
Nach dem Login kopieren

在终端执行 node test2.js 结果如下:

/*输出
{ str1: '小林别闹1', str2: '小林别闹2' }
true
*/

//改变test1.js文件变量暴露的方式
/*
exports.str1 = str1
module.exports = str2
console.log(module.exports === exports)
输出:
false
小林别闹2
*/

/*
exports.str1 = str1
module.exports.str2 = str2
console.log(module.exports === exports)
控制台输出:
true
{ str1: '小林别闹1', str2: '小林别闹2' }
*/
Nach dem Login kopieren

可以进行一下总结:

在 Node 执行一个文件时,会给这个文件内生成一个 exports 对象和一个 module 对象,而这个module 对象又有一个属性叫做 exports ,exports 是对 module.exports 的引用,它们指向同一块地址,但是最终导出的是 module.exports,第二次测试 module.exports = str2 改变了地址,所以 str1 没有导出.

另外注意,使用 exports 导出是导出一个对象

2.1.2 Es Module

Javascript 也是在不断的发展进步,这不,Es6版本就加入了Es Module模块

导出:

export const str1 = '小林别闹1'
export const str2 = '小林别闹2'
export default {
    fn() {},
    msg: "小林别闹"
}
Nach dem Login kopieren

导入:

import { st1,str2,obj } from './test.js'
Nach dem Login kopieren

注意 import,直接 nodeLassen Sie uns über Modularität und Ereignisschleife in Nodejs sprechen

Zu diesem Zeitpunkt richten wir den einfachsten Server lokal ein und der Browser dient als Client für den Zugriff

2.1 Modularisierung

Im obigen Code ist uns aufgefallen, dass es eine Anweisung wie var http = require("http"); gibt, die einführen soll http://code>-Module sind in zwei Kategorien unterteilt: Das eine ist das von Node bereitgestellte Modul, das als Kernmodul bezeichnet wird, und das andere ist das vom Benutzer geschriebene Modul, das als Dateimodul bezeichnet wird /code> ist eines der Kernmodule 1. Verwenden Sie beispielsweise das Modul <code>http zum Erstellen von Diensten, das Modul path verarbeitet Dateipfade und die url< /code>-Modul wird zum Verarbeiten und Parsen von URLs verwendet.<code>fs Modul wird zum Lesen und Schreiben von Systemdateien und Verzeichnissen usw. verwendet.

2.1. 1 CommonJS

Wenn es um Modularisierung geht, müssen wir erwähnen, dass CommonJS Node.js einen Teil der CommonJS-Syntax übernimmt, was als ein modularer Standard verstanden werden kann, um die von erzeugten Abhängigkeiten zu lösen Einführung des js-Dateicodes über das script-Tag. Die Reihenfolge ist anfällig für Fehler und Probleme wie variable Verschmutzung durch Top-Level-Bereiche

Hier können Sie das klären Unterschiede zwischen dem Exportieren von module.exports und exports

test2.js wie folgt:🎜
console.log(&#39;1&#39;)
setTimeout(() => {
  console.log(&#39;2&#39;)
})
console.log(&#39;3&#39;)//输出132
Nach dem Login kopieren
Nach dem Login kopieren
🎜Wenn test1.js wie folgt lautet:🎜
console.log(&#39;start&#39;)
setTimeout(() => {//定时器1
  console.log(&#39;timer1&#39;)
  setTimeout(function timeout () {//定时器2
    console.log(&#39;timeout&#39;);
  },0);
  setImmediate(function immediate () {//定时器3
    console.log(&#39;immediate&#39;);
  });
  Promise.resolve().then(function() {
    console.log(&#39;promise1&#39;)
  })
}, 0)
setTimeout(() => {//定时器4
  console.log(&#39;timer2&#39;)
  Promise.resolve().then(function() {
    console.log(&#39;promise2&#39;)
  })
}, 0)
Promise.resolve().then(function() {
  console.log(&#39;promise3&#39;)
})
console.log(&#39;end&#39;)
Nach dem Login kopieren
Nach dem Login kopieren
🎜Führen Sie node test2 aus .js im Terminal und das Ergebnis ist wie folgt:🎜
console.log(1)
setTimeout(()=>{console.log(2)},1000)//宏任务1
async function fn(){
    console.log(3)
    setTimeout(()=>{console.log(4)},20)//宏任务2
    //return Promise.reject()返回失败状态的,不会输出6,弄不清楚为啥
    return Promise.resolve()
}
async function run(){
    console.log(5)
    await fn()
    //console.log(6),
}
run()
//需要执行150ms左右
for(let i=0;i<90000000;i++){}
setTimeout(()=>{//宏任务3
    console.log(7)
    new Promise(resolve=>{
        console.log(8)
        resolve()
    }).then(()=>{console.log(9)})
},0)
console.log(10)
// 1 5 3 10 4 7 8 9 2
Nach dem Login kopieren
Nach dem Login kopieren
🎜Sie können eine Zusammenfassung erstellen:🎜🎜Wenn Sie eine Datei in Node ausführen, wird der Inhalt dieser Datei generiert >-Objekt und ein module-Objekt, und dieses module-Objekt hat ein Attribut namens exports, auf das exports verweist module.exports Sie verweisen auf dieselbe Adresse, aber der endgültige Export lautet module.exports. Der zweite Test module.exports = str2 wurde geändert die Adresse, daher wurde str1 nicht exportiert.🎜🎜Beachten Sie außerdem, dass mit exports zum Exportieren ein Objekt exportiert wird🎜

🎜2.1.2 Es-Modul🎜🎜🎜Javascript entwickelt sich ebenfalls ständig weiter. Nein, die Es6-Version hat das Es-Modul-Modul hinzugefügt🎜 🎜Export:🎜rrreee🎜Importieren :🎜rrreee🎜Beachten Sie, dass import, die direkte Ausführung der node js-Datei einen Fehler meldet und eine Babel-Kompilierung erfordert.🎜🎜Wenn Sie es vergleichen, ist es:🎜🎜CommonJs Anweisungen können dynamisch geladen werden und der Code wird zur Laufzeit angezeigt. Das Modul ist statisch. Anweisungen können nicht dynamisch geladen werden. Es kann nur am Anfang der Datei deklariert werden und der Code wird zur Kompilierungszeit angezeigt. 2.1.3 Module von Drittanbietern🎜🎜 🎜Zusätzlich zur Verwendung eigener Kernmodule und benutzerdefinierter Module in Node können Sie auch 🎜Module von Drittanbietern🎜🎜 verwenden

这就需要提到 npm ,npm 是 Node 的包管理工具,已经成为了世界上最大的开放源代码的生态系统,我们可以下载各种包.

当然包管理工具还有yarn,但是我暂时只用过 npm,因为它随 node 一起按照提供.

2.2 Node 的事件循环

2.2.1 非阻塞I/O

Java、PHP 或者 .NET 等服务端语言,会为每一个客户端的连接创建一个新的线程。Node 不会为每一个客户连接创建一个新的线程,而仅仅使用一个线程。

console.log(&#39;1&#39;)
setTimeout(() => {
  console.log(&#39;2&#39;)
})
console.log(&#39;3&#39;)//输出132
Nach dem Login kopieren
Nach dem Login kopieren

Javascript 的代码是从上到下一行行执行的,但是这里就不会阻塞,输出3,再输出2

2.2.2事件循环

Node 的事件循环真的好久才弄懂一丢丢,看过很多博客,觉得理解 Node 的事件循环机制,结合代码及其运行结果来分析是最容易理解的。

libuv 库负责 Node API 的执行。它将不同的任务分配给不同的线程,形成一个 Event Loop(事件循环),以异步的方式将任务的执行结果返回给 V8 引擎。其中 libuv 引擎中的事件循环分为 6 个阶段,它们会按照顺序反复运行。每当进入某一个阶段的时候,都会从对应的回调队列中取出函数去执行。当队列为空或者执行的回调函数数量到达系统设定的阈值,就会进入下一阶段。

console.log(&#39;start&#39;)
setTimeout(() => {//定时器1
  console.log(&#39;timer1&#39;)
  setTimeout(function timeout () {//定时器2
    console.log(&#39;timeout&#39;);
  },0);
  setImmediate(function immediate () {//定时器3
    console.log(&#39;immediate&#39;);
  });
  Promise.resolve().then(function() {
    console.log(&#39;promise1&#39;)
  })
}, 0)
setTimeout(() => {//定时器4
  console.log(&#39;timer2&#39;)
  Promise.resolve().then(function() {
    console.log(&#39;promise2&#39;)
  })
}, 0)
Promise.resolve().then(function() {
  console.log(&#39;promise3&#39;)
})
console.log(&#39;end&#39;)
Nach dem Login kopieren
Nach dem Login kopieren

可以 Node 上边运行一下

Lassen Sie uns über Modularität und Ereignisschleife in Nodejs sprechen

timers 阶段:这个阶段执行timer(setTimeout、setInterval)的回调

I/O callbacks 阶段:处理一些上一轮循环中的少数未执行的 I/O 回调

idle, prepare 阶段:仅node内部使用

poll 阶段:获取新的I/O事件, 适当的条件下node将阻塞在这里

check 阶段:执行 setImmediate() 的回调

close callbacks 阶段:执行 socket 的 close 事件回调

理解:首先执行同步任务,所以会输出start end,poll阶段是事件循环的入口,有异步事件就是从这里进入的,同步任务执行完执行先微任务,输出 promise3,接下来就是 setTimeout了,由 poll阶段一步步到 timers 阶段,执行定时器1,输出 timer1,将定时器2和定时器3加入到队列里边,一旦执行一个阶段里的一个任务就立刻执行微任务队列,所以再输出 promise1,然后执行定时器4,如上输出timer2,promise2,结合事件再循环,到了 check 阶段,执行 setImmediate() 的回调,输出 immediate,再循环进行,到达 timer 阶段,输出 timeout

Lassen Sie uns über Modularität und Ereignisschleife in Nodejs sprechen

2.2.3浏览器的事件循环

浏览器和 Node 的事件循环是不一样的

打算用两张图和一段代码来解释浏览器的事件循环机制,

console.log(1)
setTimeout(()=>{console.log(2)},1000)//宏任务1
async function fn(){
    console.log(3)
    setTimeout(()=>{console.log(4)},20)//宏任务2
    //return Promise.reject()返回失败状态的,不会输出6,弄不清楚为啥
    return Promise.resolve()
}
async function run(){
    console.log(5)
    await fn()
    //console.log(6),
}
run()
//需要执行150ms左右
for(let i=0;i<90000000;i++){}
setTimeout(()=>{//宏任务3
    console.log(7)
    new Promise(resolve=>{
        console.log(8)
        resolve()
    }).then(()=>{console.log(9)})
},0)
console.log(10)
// 1 5 3 10 4 7 8 9 2
Nach dem Login kopieren
Nach dem Login kopieren

执行结果如(请忽略我的工具提示):

Lassen Sie uns über Modularität und Ereignisschleife in Nodejs sprechen

我们可以储备一些前置知识:JavaScript 是单线程的,任务可以分为同步任务和异步任务,像 console.log('1') 就是同步的,定时器 setTimeout,promise 的回调等就是异步的。同步的很好理解,就从上到下一行一行的执行下来,异步的就有点小复杂了,还会分为宏任务和微任务。

浏览器的事件循环机制就是:先执行同步任务,同步任务执行完成,就执行任务队列里面的任务,那任务队列里面的任务是哪来的呢?异步任务准备好了就会放进任务队列,你可以理解为,在任务队列里边宏任务和微任务都存在这一个队列结构管着它们。先后的话,同步任务执行完成后,任务队列里有微任务,则将微任务执行完,再执行一个宏任务,执行了宏任务可能又产生了微任务,这是就需要再执行完微任务任务。你可以将同步任务看成宏任务,这样就可以理解为,每执行完一个宏任务都要清理一遍微任务。

Lassen Sie uns über Modularität und Ereignisschleife in Nodejs sprechen

Lassen Sie uns über Modularität und Ereignisschleife in Nodejs sprechen

Der obige Code wird wie folgt erklärt: Führen Sie die erste Codezeile aus und geben Sie sie aus. 1,执行到第二行代码 setTimeout 属于宏任务1,准备1000毫秒后加入任务队列,然后执行函数 run,输出 5,因为 await 的存在,我们需要等待 fn函数执行完毕,这里是通过 await 关键字将异步函数变成同步的,执行 fn 时输出 3,又出现一个 setTimeout 宏任务2,准备时间20毫秒,返回成功状态的Promise,输出 6,for 循环需要150ms,这是宏任务2,准备完毕,进入任务队列,继续向下,有一个 setTimeout 宏任务3,无需准备加入任务队列,执行最后一行代码,输出 10,至此同步任务全部执行完毕,接下来是异步任务了,任务队列是队列的数据结构,遵循先进先出的原则,此时任务队列中有宏任务2和宏任务3,先执行宏任务2,输出 4,再执行宏任务3,输出 7promise本身是同步的,输出 8,回调then 里边的代码是微任务,宏任务3执行后,发现有微任务存在,清理一边微任务,输出 9,整个流程经过1000毫秒后,宏任务1加入任务队列,输出 2

Es ist möglicherweise einfacher zu verstehen, wenn Sie sich den Blog eines großen Bruders ansehen. Tut mir leid, der Link ist unten.

Ich schäme mich, wenn ich nicht gut schreibe, aber ich werde fleißig lernen! ! !

Weitere Kenntnisse zum Thema Programmierung finden Sie unter: Programmiervideos! !

Das obige ist der detaillierte Inhalt vonLassen Sie uns über Modularität und Ereignisschleife in Nodejs sprechen. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Verwandte Etiketten:
Quelle:juejin.cn
Erklärung dieser Website
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn
Beliebte Tutorials
Mehr>
Neueste Downloads
Mehr>
Web-Effekte
Quellcode der Website
Website-Materialien
Frontend-Vorlage