本篇文章帶大家來了解Nodejs中的模組化和事件循環。有一定的參考價值,有需要的朋友可以參考一下,希望對大家有幫助。
5.20出了線上Ide,能夠在瀏覽器上邊執行Node.js —WebContainers
Node.js 到底是什麼?開始學習的時候,對於前端的一些知識領域沒有太多的接觸(當然現在也一樣),對於 Node.js 的印象就是,它和Javascript 的語法幾乎一樣,然後是寫後端的。記得當時還竊喜,學了 Javascript = 啥都會了!好了切入正題
【推薦學習:《nodejs 教程》】
以前Javascript 都是運行在瀏覽器上邊的,Javascript 是一種高級語言,計算機不能直接讀懂,畢竟二進制的計算機的世界裡邊就只有010101...,在這個時候瀏覽器中的JavaScript 引擎,就充當了翻譯官的角色,把JavaScript 想要做什麼手把手翻譯給計算機,這其中的編譯過程就不細說(我暫時也說不清楚)。
Node.js 基於 Chrome 瀏覽器的 V8 引擎,可以有效率的編譯 Javascript,所以可以說 Node.js 是瀏覽器以外的另一個 Javascript 運作環境。
記得在騰訊雲的雲函數上折騰過微信公眾號的簡單的自動回复,當時對前端代碼的模組化有了小小的體會,Node.js 的功勞!
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/
就能看到頁面列印出來:
var http = require("http"); 這樣的語句,作用是引入
http 模組. 在Node 中,模組分為兩類:一是Node 提供的模組,稱為核心模組;二是使用者編寫的模組,稱為檔案模組.
http 就是核心模組之一,例如使用
http 模組可以創建服務,
path 模組處理檔案路徑,
url 模組用於處理與解析URL.
fs模組用於對系統檔案及目錄進行讀寫操作等.
script標籤引入
js檔案程式碼產生的依賴順序易出錯,頂層作用域導致的變數污染等問題
module. exports 和
exports 的差異
let str = require('./test1'); console.log(str)
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' } */
exports#物件和一個
module 物件,而這個
module 物件又有一個屬性叫做
exports ,
exports 是對
module.exports 的引用,它們指向同一塊位址,但是最終導出的是
module.exports,第二次測試
module.exports = str2 改變了位址,所以
str1 沒有導出.
exports 導出是導出一個物件
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 第三方模組
这就需要提到 当然包管理工具还有 Java、PHP 或者 .NET 等服务端语言,会为每一个客户端的连接创建一个新的线程。Node 不会为每一个客户连接创建一个新的线程,而仅仅使用一个线程。 Javascript 的代码是从上到下一行行执行的,但是这里就不会阻塞,输出3,再输出2 Node 的事件循环真的好久才弄懂一丢丢,看过很多博客,觉得理解 Node 的事件循环机制,结合代码及其运行结果来分析是最容易理解的。 libuv 库负责 Node API 的执行。它将不同的任务分配给不同的线程,形成一个 Event Loop(事件循环),以异步的方式将任务的执行结果返回给 V8 引擎。其中 libuv 引擎中的事件循环分为 6 个阶段,它们会按照顺序反复运行。每当进入某一个阶段的时候,都会从对应的回调队列中取出函数去执行。当队列为空或者执行的回调函数数量到达系统设定的阈值,就会进入下一阶段。 可以 Node 上边运行一下 timers 阶段:这个阶段执行timer(setTimeout、setInterval)的回调 I/O callbacks 阶段:处理一些上一轮循环中的少数未执行的 I/O 回调 idle, prepare 阶段:仅node内部使用 poll 阶段:获取新的I/O事件, 适当的条件下node将阻塞在这里 check 阶段:执行 setImmediate() 的回调 close callbacks 阶段:执行 socket 的 close 事件回调 理解:首先执行同步任务,所以会输出 浏览器和 Node 的事件循环是不一样的 打算用两张图和一段代码来解释浏览器的事件循环机制, 执行结果如(请忽略我的工具提示): 我们可以储备一些前置知识:JavaScript 是单线程的,任务可以分为同步任务和异步任务,像 浏览器的事件循环机制就是:先执行同步任务,同步任务执行完成,就执行任务队列里面的任务,那任务队列里面的任务是哪来的呢?异步任务准备好了就会放进任务队列,你可以理解为,在任务队列里边宏任务和微任务都存在这一个队列结构管着它们。先后的话,同步任务执行完成后,任务队列里有微任务,则将微任务执行完,再执行一个宏任务,执行了宏任务可能又产生了微任务,这是就需要再执行完微任务任务。你可以将同步任务看成宏任务,这样就可以理解为,每执行完一个宏任务都要清理一遍微任务。 上邊程式碼解釋如下:執行到第一行程式碼,輸出 這裡直接看一位大哥的部落格可能更容易理解,寫不了那麼好,哭,連結放在下面了。 寫的不好,羞愧萬分,會努力學習的! ! ! 更多程式相關知識,請造訪:程式設計影片! ! 以上是聊聊Nodejs中的模組化和事件循環的詳細內容。更多資訊請關注PHP中文網其他相關文章!npm
,npm
是 Node 的包管理工具,已经成为了世界上最大的开放源代码的生态系统,我们可以下载各种包.yarn
,但是我暂时只用过 npm
,因为它随 node
一起按照提供.2.2 Node 的事件循环
2.2.1 非阻塞I/O
console.log('1')
setTimeout(() => {
console.log('2')
})
console.log('3')//输出132
2.2.2事件循环
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')
start end
,poll阶段是事件循环的入口,有异步事件就是从这里进入的,同步任务执行完执行先微任务,输出 promise3
,接下来就是 setTimeout
了,由 poll
阶段一步步到 timers
阶段,执行定时器1,输出 timer1
,将定时器2和定时器3加入到队列里边,一旦执行一个阶段里的一个任务就立刻执行微任务队列,所以再输出 promise1
,然后执行定时器4,如上输出timer2,promise2
,结合事件再循环,到了 check
阶段,执行 setImmediate() 的回调,输出 immediate
,再循环进行,到达 timer
阶段,输出 timeout
2.2.3浏览器的事件循环
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
console.log('1')
就是同步的,定时器 setTimeout
,promise
的回调等就是异步的。同步的很好理解,就从上到下一行一行的执行下来,异步的就有点小复杂了,还会分为宏任务和微任务。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,輸出7
,promise
本身是同步的,輸出8
,回呼then
裡邊的程式碼是微任務,巨集任務3執行後,發現有微任務存在,清理一邊微任務,輸出9
,整個流程經過1000毫秒後,巨集任務1加入任務佇列,輸出2