搜尋
首頁web前端js教程聊聊Nodejs中的模組化和事件循環
聊聊Nodejs中的模組化和事件循環Jun 01, 2021 am 11:06 AM
nodejs事件循環模組化

本篇文章帶大家來了解Nodejs中的模組化和事件循環。有一定的參考價值,有需要的朋友可以參考一下,希望對大家有幫助。

聊聊Nodejs中的模組化和事件循環

5.20出了線上Ide,能夠在瀏覽器上邊執行Node.js —WebContainers

1 Node.js簡介

Node.js 到底是什麼?開始學習的時候,對於前端的一些知識領域沒有太多的接觸(當然現在也一樣),對於 Node.js 的印象就是,它和Javascript 的語法幾乎一樣,然後是寫後端的。記得當時還竊喜,學了 Javascript = 啥都會了!好了切入正題

【推薦學習:《nodejs 教程》】

以前Javascript 都是運行在瀏覽器上邊的,Javascript 是一種高級語言,計算機不能直接讀懂,畢竟二進制的計算機的世界裡邊就只有010101...,在這個時候瀏覽器中的JavaScript 引擎,就充當了翻譯官的角色,把JavaScript 想要做什麼手把手翻譯給計算機,這其中的編譯過程就不細說(我暫時也說不清楚)。

Node.js 基於 Chrome 瀏覽器的 V8 引擎,可以有效率的編譯 Javascript,所以可以說 Node.js 是瀏覽器以外的另一個 Javascript 運作環境。

記得在騰訊雲的雲函數上折騰過微信公眾號的簡單的自動回复,當時對前端代碼的模組化有了小小的體會,Node.js 的功勞!

2 初步體驗

server.js 檔案如下

// 引入 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端口

安裝了Node 的前提下在終端機執行node server.js 開啟瀏覽器,在網址列輸入http://localhost:3000/ 就能看到頁面列印出來:

聊聊Nodejs中的模組化和事件循環

##此時我們在本地搭建起一個最簡單的伺服器,瀏覽器作為客戶端進行訪問

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)

當test1.js如下:

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

exports.str1 = str1
exports.str2 = str2
console.log(module.exports === exports)

在終端執行

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' }
*/

可以進行一下總結:

在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: "小林别闹"
}

導入:

import { st1,str2,obj } from './test.js'

注意

import

,直接node js 檔案執行會錯誤的,需要babel 編譯比較一下的話就是:

CommonJs 可以動態載入語句,程式碼發生在執行時期

Es Module 是靜態的,不可以動態載入語句,只能宣告在該檔案的最頂部,程式碼發生在編譯時

#2.1.3 第三方模組

在Node 中除了可以使用自己提供的核心模組,自訂模組,還可以使用

第三方模組

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

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

2.2  Node 的事件循环

2.2.1 非阻塞I/O

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

console.log('1')
setTimeout(() => {
  console.log('2')
})
console.log('3')//输出132

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

2.2.2事件循环

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

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

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

可以 Node 上边运行一下

聊聊Nodejs中的模組化和事件循環

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

聊聊Nodejs中的模組化和事件循環

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

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

聊聊Nodejs中的模組化和事件循環

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

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

聊聊Nodejs中的模組化和事件循環

聊聊Nodejs中的模組化和事件循環

上邊程式碼解釋如下:執行到第一行程式碼,輸出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

這裡直接看一位大哥的部落格可能更容易理解,寫不了那麼好,哭,連結放在下面了。

寫的不好,羞愧萬分,會努力學習的! ! !

更多程式相關知識,請造訪:程式設計影片! !

以上是聊聊Nodejs中的模組化和事件循環的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述
本文轉載於:掘金社区。如有侵權,請聯絡admin@php.cn刪除
Vercel是什么?怎么部署Node服务?Vercel是什么?怎么部署Node服务?May 07, 2022 pm 09:34 PM

Vercel是什么?本篇文章带大家了解一下Vercel,并介绍一下在Vercel中部署 Node 服务的方法,希望对大家有所帮助!

node.js gm是什么node.js gm是什么Jul 12, 2022 pm 06:28 PM

gm是基于node.js的图片处理插件,它封装了图片处理工具GraphicsMagick(GM)和ImageMagick(IM),可使用spawn的方式调用。gm插件不是node默认安装的,需执行“npm install gm -S”进行安装才可使用。

聊聊Node.js中的多进程和多线程聊聊Node.js中的多进程和多线程Jul 25, 2022 pm 07:45 PM

大家都知道 Node.js 是单线程的,却不知它也提供了多进(线)程模块来加速处理一些特殊任务,本文便带领大家了解下 Node.js 的多进(线)程,希望对大家有所帮助!

火了!新的JavaScript运行时:Bun,性能完爆Node火了!新的JavaScript运行时:Bun,性能完爆NodeJul 15, 2022 pm 02:03 PM

今天跟大家介绍一个最新开源的 javaScript 运行时:Bun.js。比 Node.js 快三倍,新 JavaScript 运行时 Bun 火了!

nodejs中lts是什么意思nodejs中lts是什么意思Jun 29, 2022 pm 03:30 PM

在nodejs中,lts是长期支持的意思,是“Long Time Support”的缩写;Node有奇数版本和偶数版本两条发布流程线,当一个奇数版本发布后,最近的一个偶数版本会立即进入LTS维护计划,一直持续18个月,在之后会有12个月的延长维护期,lts期间可以支持“bug fix”变更。

node爬取数据实例:聊聊怎么抓取小说章节node爬取数据实例:聊聊怎么抓取小说章节May 02, 2022 am 10:00 AM

node怎么爬取数据?下面本篇文章给大家分享一个node爬虫实例,聊聊利用node抓取小说章节的方法,希望对大家有所帮助!

深入浅析Nodejs中的net模块深入浅析Nodejs中的net模块Apr 11, 2022 pm 08:40 PM

本篇文章带大家带大家了解一下Nodejs中的net模块,希望对大家有所帮助!

怎么获取Node性能监控指标?获取方法分享怎么获取Node性能监控指标?获取方法分享Apr 19, 2022 pm 09:25 PM

怎么获取Node性能监控指标?本篇文章来和大家聊聊Node性能监控指标获取方法,希望对大家有所帮助!

See all articles

熱AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover

AI Clothes Remover

用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool

Undress AI Tool

免費脫衣圖片

Clothoff.io

Clothoff.io

AI脫衣器

AI Hentai Generator

AI Hentai Generator

免費產生 AI 無盡。

熱門文章

R.E.P.O.能量晶體解釋及其做什麼(黃色晶體)
2 週前By尊渡假赌尊渡假赌尊渡假赌
倉庫:如何復興隊友
1 個月前By尊渡假赌尊渡假赌尊渡假赌
Hello Kitty Island冒險:如何獲得巨型種子
4 週前By尊渡假赌尊渡假赌尊渡假赌

熱工具

Dreamweaver CS6

Dreamweaver CS6

視覺化網頁開發工具

禪工作室 13.0.1

禪工作室 13.0.1

強大的PHP整合開發環境

EditPlus 中文破解版

EditPlus 中文破解版

體積小,語法高亮,不支援程式碼提示功能

SublimeText3 英文版

SublimeText3 英文版

推薦:為Win版本,支援程式碼提示!

ZendStudio 13.5.1 Mac

ZendStudio 13.5.1 Mac

強大的PHP整合開發環境