這期內容當中小編將會給大家?guī)碛嘘P使用Nodejs怎么實現(xiàn)模塊化和事件循環(huán),文章內容豐富且以專業(yè)的角度為大家分析和敘述,閱讀完這篇文章希望大家可以有所收獲。
專注于為中小企業(yè)提供成都網(wǎng)站制作、成都網(wǎng)站設計、外貿營銷網(wǎng)站建設服務,電腦端+手機端+微信端的三站合一,更高效的管理,為中小企業(yè)南通免費做網(wǎng)站提供優(yōu)質的服務。我們立足成都,凝聚了一批互聯(lián)網(wǎng)行業(yè)人才,有力地推動了近1000家企業(yè)的穩(wěn)健成長,幫助中小企業(yè)通過網(wǎng)站建設實現(xiàn)規(guī)模擴充和轉變。
Node.js 到底是什么?開始學習的時候,對于前端的一些知識領域沒有太多的接觸(當然現(xiàn)在也一樣),對于 Node.js 的印象就是,它和Javascript 的語法幾乎一樣,然后是寫后端的。記得當時還竊喜,學了 Javascript = 啥都會了!好了切入正題
以前 Javascript 都是運行在瀏覽器上邊的,Javascript 是一種高級語言,計算機不能直接讀懂,畢竟二進制的計算機的世界里邊就只有010101...,在這個時候瀏覽器中的 JavaScript 引擎,就充當了翻譯官的角色,把 JavaScript 想要做什么手把手翻譯給計算機,這其中的編譯過程就不細說(我暫時也說不清楚)。
Node.js 基于 Chrome 瀏覽器的 V8 引擎,可以高效的編譯 Javascript,所以可以說 Node.js 是除瀏覽器以外的另一個 Javascript 運行環(huán)境。
記得在騰訊云的云函數(shù)上折騰過微信公眾號的簡單的自動回復,當時對前端代碼的模塊化有了小小的體會,Node.js 的功勞!
server.js 文件如下
// 引入 http 模塊
var http = require("http");
// 用 http 模塊創(chuàng)建服務
//req 獲取 url 信息 (request)
//res 瀏覽器返回響應信息 (response)
http.createServer(function (req, res) {
// 設置 HTTP 頭部,狀態(tài)碼是 200,文件類型是 html,字符集是 utf8
//Content-Type字段用于定義網(wǎng)絡文件的類型和網(wǎng)頁的編碼,決定瀏覽器將以什么形式、什么編碼讀取這個文件,不寫就可能會出現(xiàn)亂碼哦
res.writeHead(200, {
"Content-Type": "text/html;charset=UTF-8"
});
// 往頁面打印值
res.write('小林別鬧');
// 結束響應
res.end();
}).listen(3000); // 監(jiān)聽3000端口安裝了 Node 的前提下在終端運行 node server.js 打開瀏覽器,在地址欄輸入http://localhost:3000/ 就能看到頁面打印出來:

此時我們在本地搭建起一個最簡單的服務器,瀏覽器作為客戶端進行訪問
在上邊的代碼中,我們注意到了有 var http = require("http"); 這樣的語句,作用是引入 http 模塊. 在 Node 中,模塊分為兩類:一是 Node 提供的模塊,稱為核心模塊;二是用戶編寫的模塊,稱為文件模塊.http 就是核心模塊之一,例如使用 http 模塊可以創(chuàng)建服務,path 模塊處理文件路徑,url 模塊用于處理與解析 URL.fs模塊用于對系統(tǒng)文件及目錄進行讀寫操作等.
提到模塊化,就必須提一嘴 CommonJS,Node.js 就采用了部分 CommonJS 語法,可以理解為 CommonJS 是一種模塊化的標準.在早期為了解決通過script標簽引入js文件代碼產生的依賴順序易出錯,頂層作用域導致的變量污染等問題
在這里可以梳理一下導出 module.exports 和 exports 的差別
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)
在終端執(zhí)行 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 執(zhí)行一個文件時,會給這個文件內生成一個 exports 對象和一個 module 對象,而這個module 對象又有一個屬性叫做 exports ,exports 是對 module.exports 的引用,它們指向同一塊地址,但是最終導出的是 module.exports,第二次測試 module.exports = str2 改變了地址,所以 str1 沒有導出.
另外注意,使用 exports 導出是導出一個對象
Javascript 也是在不斷的發(fā)展進步,這不,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 文件執(zhí)行會報錯的,需要 babel 編譯
比較一下的話就是:
CommonJs 可以動態(tài)加載語句,代碼發(fā)生在運行時
Es Module 是靜態(tài)的,不可以動態(tài)加載語句,只能聲明在該文件的最頂部,代碼發(fā)生在編譯時
在Node 中除了可以使用自己提供的核心模塊,自定義模塊,還可以使用第三方模塊
這就需要提到 npm ,npm 是 Node 的包管理工具,已經成為了世界上最大的開放源代碼的生態(tài)系統(tǒng),我們可以下載各種包.
當然包管理工具還有yarn,但是我暫時只用過 npm,因為它隨 node 一起按照提供.
Java、PHP 或者 .NET 等服務端語言,會為每一個客戶端的連接創(chuàng)建一個新的線程。Node 不會為每一個客戶連接創(chuàng)建一個新的線程,而僅僅使用一個線程。
console.log('1')
setTimeout(() => {
console.log('2')
})
console.log('3')//輸出132Javascript 的代碼是從上到下一行行執(zhí)行的,但是這里就不會阻塞,輸出3,再輸出2
Node 的事件循環(huán)真的好久才弄懂一丟丟,看過很多博客,覺得理解 Node 的事件循環(huán)機制,結合代碼及其運行結果來分析是最容易理解的。
libuv 庫負責 Node API 的執(zhí)行。它將不同的任務分配給不同的線程,形成一個 Event Loop(事件循環(huán)),以異步的方式將任務的執(zhí)行結果返回給 V8 引擎。其中 libuv 引擎中的事件循環(huán)分為 6 個階段,它們會按照順序反復運行。每當進入某一個階段的時候,都會從對應的回調隊列中取出函數(shù)去執(zhí)行。當隊列為空或者執(zhí)行的回調函數(shù)數(shù)量到達系統(tǒng)設定的閾值,就會進入下一階段。
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 上邊運行一下

timers 階段:這個階段執(zhí)行timer(setTimeout、setInterval)的回調
I/O callbacks 階段:處理一些上一輪循環(huán)中的少數(shù)未執(zhí)行的 I/O 回調
idle, prepare 階段:僅node內部使用
poll 階段:獲取新的I/O事件, 適當?shù)臈l件下node將阻塞在這里
check 階段:執(zhí)行 setImmediate() 的回調
close callbacks 階段:執(zhí)行 socket 的 close 事件回調
理解:首先執(zhí)行同步任務,所以會輸出start end,poll階段是事件循環(huán)的入口,有異步事件就是從這里進入的,同步任務執(zhí)行完執(zhí)行先微任務,輸出 promise3,接下來就是 setTimeout了,由 poll階段一步步到 timers 階段,執(zhí)行定時器1,輸出 timer1,將定時器2和定時器3加入到隊列里邊,一旦執(zhí)行一個階段里的一個任務就立刻執(zhí)行微任務隊列,所以再輸出 promise1,然后執(zhí)行定時器4,如上輸出timer2,promise2,結合事件再循環(huán),到了 check 階段,執(zhí)行 setImmediate() 的回調,輸出 immediate,再循環(huán)進行,到達 timer 階段,輸出 timeout

瀏覽器和 Node 的事件循環(huán)是不一樣的
打算用兩張圖和一段代碼來解釋瀏覽器的事件循環(huán)機制,
console.log(1)
setTimeout(()=>{console.log(2)},1000)//宏任務1
async function fn(){
console.log(3)
setTimeout(()=>{console.log(4)},20)//宏任務2
//return Promise.reject()返回失敗狀態(tài)的,不會輸出6,弄不清楚為啥
return Promise.resolve()
}
async function run(){
console.log(5)
await fn()
//console.log(6),
}
run()
//需要執(zhí)行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執(zhí)行結果如(請忽略我的工具提示):

我們可以儲備一些前置知識:JavaScript 是單線程的,任務可以分為同步任務和異步任務,像 console.log('1') 就是同步的,定時器 setTimeout,promise 的回調等就是異步的。同步的很好理解,就從上到下一行一行的執(zhí)行下來,異步的就有點小復雜了,還會分為宏任務和微任務。
瀏覽器的事件循環(huán)機制就是:先執(zhí)行同步任務,同步任務執(zhí)行完成,就執(zhí)行任務隊列里面的任務,那任務隊列里面的任務是哪來的呢?異步任務準備好了就會放進任務隊列,你可以理解為,在任務隊列里邊宏任務和微任務都存在這一個隊列結構管著它們。先后的話,同步任務執(zhí)行完成后,任務隊列里有微任務,則將微任務執(zhí)行完,再執(zhí)行一個宏任務,執(zhí)行了宏任務可能又產生了微任務,這是就需要再執(zhí)行完微任務任務。你可以將同步任務看成宏任務,這樣就可以理解為,每執(zhí)行完一個宏任務都要清理一遍微任務。


上邊代碼解釋如下:執(zhí)行到第一行代碼,輸出 1,執(zhí)行到第二行代碼 setTimeout 屬于宏任務1,準備1000毫秒后加入任務隊列,然后執(zhí)行函數(shù) run,輸出 5,因為 await 的存在,我們需要等待 fn函數(shù)執(zhí)行完畢,這里是通過 await 關鍵字將異步函數(shù)變成同步的,執(zhí)行 fn 時輸出 3,又出現(xiàn)一個 setTimeout 宏任務2,準備時間20毫秒,返回成功狀態(tài)的Promise,輸出 6,for 循環(huán)需要150ms,這是宏任務2,準備完畢,進入任務隊列,繼續(xù)向下,有一個 setTimeout 宏任務3,無需準備加入任務隊列,執(zhí)行最后一行代碼,輸出 10,至此同步任務全部執(zhí)行完畢,接下來是異步任務了,任務隊列是隊列的數(shù)據(jù)結構,遵循先進先出的原則,此時任務隊列中有宏任務2和宏任務3,先執(zhí)行宏任務2,輸出 4,再執(zhí)行宏任務3,輸出 7,promise本身是同步的,輸出 8,回調then 里邊的代碼是微任務,宏任務3執(zhí)行后,發(fā)現(xiàn)有微任務存在,清理一邊微任務,輸出 9,整個流程經過1000毫秒后,宏任務1加入任務隊列,輸出 2
上述就是小編為大家分享的使用Nodejs怎么實現(xiàn)模塊化和事件循環(huán)了,如果剛好有類似的疑惑,不妨參照上述分析進行理解。如果想知道更多相關知識,歡迎關注創(chuàng)新互聯(lián)行業(yè)資訊頻道。
網(wǎng)頁標題:使用Nodejs怎么實現(xiàn)模塊化和事件循環(huán)
本文鏈接:http://www.chinadenli.net/article24/peegce.html
成都網(wǎng)站建設公司_創(chuàng)新互聯(lián),為您提供做網(wǎng)站、虛擬主機、外貿網(wǎng)站建設、網(wǎng)站策劃、定制網(wǎng)站、
聲明:本網(wǎng)站發(fā)布的內容(圖片、視頻和文字)以用戶投稿、用戶轉載內容為主,如果涉及侵權請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內容未經允許不得轉載,或轉載時需注明來源: 創(chuàng)新互聯(lián)