深入理解Node.js 事件循環(huán)和回調(diào)函數(shù)
本文詳細(xì)的介紹了Node.js 事件循環(huán)和Node.js回調(diào)函數(shù),廢話不多說(shuō)了,具體看下面把。
一、Node.js 事件循環(huán)
Node.js 是單進(jìn)程單線程應(yīng)用程序,但是通過(guò)事件和回調(diào)支持并發(fā),所以性能非常高。Node.js 的每一個(gè) API 都是異步的,并作為一個(gè)獨(dú)立線程運(yùn)行,使用異步函數(shù)調(diào)用,并處理并發(fā)。Node.js 基本上所有的事件機(jī)制都是用設(shè)計(jì)模式中觀察者模式實(shí)現(xiàn)。Node.js 單線程類似進(jìn)入一個(gè)while(true)的事件循環(huán),直到?jīng)]有事件觀察者退出,每個(gè)異步事件都生成一個(gè)事件觀察者,如果有事件發(fā)生就調(diào)用該回調(diào)函數(shù).
1、事件驅(qū)動(dòng)程序
Node.js 使用事件驅(qū)動(dòng)模型,當(dāng)web server接收到請(qǐng)求,就把它關(guān)閉然后進(jìn)行處理,然后去服務(wù)下一個(gè)web請(qǐng)求。當(dāng)這個(gè)請(qǐng)求完成,它被放回處理隊(duì)列,當(dāng)?shù)竭_(dá)隊(duì)列開(kāi)頭,這個(gè)結(jié)果被返回給用戶。這個(gè)模型非常高效可擴(kuò)展性非常強(qiáng),因?yàn)閣eb server一直接受請(qǐng)求而不等待任何讀寫(xiě)操作。(這也被稱之為非阻塞式IO或者事件驅(qū)動(dòng)IO)。在事件驅(qū)動(dòng)模型中,會(huì)生成一個(gè)主循環(huán)來(lái)監(jiān)聽(tīng)事件,當(dāng)檢測(cè)到事件時(shí)觸發(fā)回調(diào)函數(shù)。
整個(gè)事件驅(qū)動(dòng)的流程就是這么實(shí)現(xiàn)的,非常簡(jiǎn)潔。有點(diǎn)類似于觀察者模式,事件相當(dāng)于一個(gè)主題(Subject),而所有注冊(cè)到這個(gè)事件上的處理函數(shù)相當(dāng)于觀察者(Observer)。Node.js 有多個(gè)內(nèi)置的事件,我們可以通過(guò)引入 events 模塊,并通過(guò)實(shí)例化 EventEmitter 類來(lái)綁定和監(jiān)聽(tīng)事件,如下實(shí)例:
// 引入 events 模塊 var events = require('events'); // 創(chuàng)建 eventEmitter 對(duì)象 var eventEmitter = new events.EventEmitter(); 以下程序綁定事件處理程序: // 綁定事件及事件的處理程序 eventEmitter.on('eventName', eventHandler); 我們可以通過(guò)程序觸發(fā)事件: // 觸發(fā)事件 eventEmitter.emit('eventName');
2、實(shí)例
創(chuàng)建 main.js 文件,代碼如下所示:
// 引入 events 模塊 var events = require('events'); // 創(chuàng)建 eventEmitter 對(duì)象 var eventEmitter = new events.EventEmitter(); // 創(chuàng)建事件處理程序 var connectHandler = function connected() { console.log('連接成功。'); // 觸發(fā) data_received 事件 eventEmitter.emit('data_received'); } // 綁定 connection 事件處理程序 eventEmitter.on('connection', connectHandler); // 使用匿名函數(shù)綁定 data_received 事件 eventEmitter.on('data_received', function(){ console.log('數(shù)據(jù)接收成功。'); }); // 觸發(fā) connection 事件 eventEmitter.emit('connection'); console.log("程序執(zhí)行完畢。");
二、Node.js 回調(diào)函數(shù)
Node.js 異步編程的直接體現(xiàn)就是回調(diào)。異步編程依托于回調(diào)來(lái)實(shí)現(xiàn),但不能說(shuō)使用了回調(diào)后程序就異步化了?;卣{(diào)函數(shù)在完成任務(wù)后就會(huì)被調(diào)用,Node 使用了大量的回調(diào)函數(shù),Node 所有 API 都支持回調(diào)函數(shù)。例如,我們可以一邊讀取文件,一邊執(zhí)行其他命令,在文件讀取完成后,我們將文件內(nèi)容作為回調(diào)函數(shù)的參數(shù)返回。這樣在執(zhí)行代碼時(shí)就沒(méi)有阻塞或等待文件 I/O 操作。這就大大提高了 Node.js 的性能,可以處理大量的并發(fā)請(qǐng)求。
1、阻塞代碼實(shí)例
創(chuàng)建一個(gè)文件 test.txt ,內(nèi)容如下:
Hello World! fs.readFileSync() fs.readFile()
創(chuàng)建 test.js 文件, 代碼如下:
console.log('-------程序開(kāi)始執(zhí)行--------'); // 引入fs模塊 var fs = require("fs"); //同步讀取文件 var data = fs.readFileSync('test.txt','utf-8'); console.log(data.toString()); console.log('-------程序執(zhí)行結(jié)束--------');
以上代碼執(zhí)行結(jié)果如下:
2、非阻塞代碼實(shí)例
創(chuàng)建 test.js 文件, 代碼如下:
console.log('-------程序開(kāi)始執(zhí)行--------'); // 引入fs模塊 var fs = require("fs"); //異步讀取文件 fs.readFile('test.txt','utf-8',function (err, data) { if (err) return console.error(err); console.log(data.toString()); }); console.log('-------程序執(zhí)行結(jié)束--------');
以上程序中 fs.readFile() 是異步函數(shù)用于讀取文件。如果在讀取文件過(guò)程中發(fā)生錯(cuò)誤,錯(cuò)誤 err 對(duì)象就會(huì)輸出錯(cuò)誤信息。如果沒(méi)發(fā)生錯(cuò)誤,readFile 跳過(guò) err 對(duì)象的輸出,文件內(nèi)容就通過(guò)回調(diào)函數(shù)輸出。
以上代碼執(zhí)行結(jié)果如下:
接下來(lái)我們刪除 input.txt 文件,執(zhí)行結(jié)果如下所示:
以上兩個(gè)實(shí)例我們了解了阻塞與非阻塞調(diào)用的不同。第一個(gè)實(shí)例在文件讀取完后才執(zhí)行完程序。第二個(gè)實(shí)例我們不需要等待文件讀取完,這樣就可以在讀取文件時(shí)同時(shí)執(zhí)行接下來(lái)的代碼,大大提高了程序的性能。因此,阻塞按是按順序執(zhí)行的,而非阻塞是不需要按順序的,所以如果需要處理回調(diào)函數(shù)的參數(shù),我們就需要寫(xiě)在回調(diào)函數(shù)內(nèi)。
三、fs.readFileSync和fs.readFile
1、s.readFileSync
語(yǔ)法:fs.readFileSync(filename, [encoding])
接收參數(shù):
filename:文件路徑
options:option對(duì)象,包含 encoding,編碼格式,該項(xiàng)是可選的。
由于Node.js僅支持如下編碼:utf8, ucs2, ascii, binary, base64, hex,并不支持中文GBK或GB2312之類的編碼,因此如果要讀寫(xiě)GBK或GB2312格式的文件的中文內(nèi)容,必須要用額外的模塊:iconv-lite。
2、fs.readFile
語(yǔ)法:fs.readFile(filename, [encoding], [callback(err,data)])
接收參數(shù):
filename:文件路徑
options :option對(duì)象,包含 encoding,編碼格式,該項(xiàng)是可選的。
callback :回調(diào),傳遞2個(gè)參數(shù) 異常err 和 文件內(nèi)容 data
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
node.js中使用ejs渲染數(shù)據(jù)的代碼實(shí)現(xiàn)
這篇文章主要介紹了node.js中使用ejs渲染數(shù)據(jù),本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友參考下吧2023-11-11NodeJs讀取JSON文件格式化時(shí)的注意事項(xiàng)
本文是作者在進(jìn)行NodeJs開(kāi)發(fā)時(shí)偶然發(fā)現(xiàn)的問(wèn)題,經(jīng)過(guò)一番努力,最終找到解決方案,分享給大家,有需要的小伙伴可以參考下2016-09-09詳解nodejs爬蟲(chóng)程序解決gbk等中文編碼問(wèn)題
本篇文章主要介紹了nodejs爬蟲(chóng)程序解決gbk等中文編碼問(wèn)題,解決了網(wǎng)頁(yè)的編碼與nodejs默認(rèn)編碼不一致造成的亂碼問(wèn)題,有興趣的可以了解一下2017-04-04