我的Node.js學(xué)習(xí)之路(三)--node.js作用、回調(diào)、同步和異步代碼 以及事件循環(huán)
一,node.js的作用,
I/O的意義,(I/O是輸入/輸出的簡(jiǎn)寫(xiě),如:鍵盤(pán)敲入文本,輸入,屏幕上看到文本顯示輸出。鼠標(biāo)移動(dòng),在屏幕上看到鼠標(biāo)的移動(dòng)。終端的輸入,和看到的輸出。等等)
node.js想解決的問(wèn)題,(處理輸入,輸入,高并發(fā) 。如 在線游戲中可能會(huì)有上百萬(wàn)個(gè)游戲者,則有上百萬(wàn)的輸入等等)(node.js適合的范疇:當(dāng)應(yīng)用程序需要在網(wǎng)絡(luò)上發(fā)送和接收數(shù)據(jù)時(shí)Node.js最為適合。這可能是第三方的API,聯(lián)網(wǎng)設(shè)備或者瀏覽器與服務(wù)器之間的實(shí)時(shí)通信)
并發(fā)的意義,(并發(fā)這個(gè)術(shù)語(yǔ)描述的是事情會(huì)在同時(shí)發(fā)生并可能相互交互。Node的事件化的I/O模型讓我們無(wú)需擔(dān)心互鎖和并發(fā)這兩個(gè)在多線程異步I/O中常見(jiàn)的問(wèn)題)
演示網(wǎng)絡(luò)I/O
Js代碼
var http = require('http'), urls = ['www.baidu.com','www.10jqka.com.cn','www.duokan.com']; function fetchPage(url){ var start = new Date(); http.get({host:url},function(res){ console.log("Got response from:" + url); console.log("Request took:",new Date() - start, "ms"); }); } for(var i=0; i<urls.length; i++){ fetchPage(urls[i]); }
命名為,node.js
我們?cè)诮K端里面運(yùn)行node node.js
輸出:
我們要求node.js訪問(wèn)三個(gè)url并報(bào)告收到響應(yīng)的情況以及所耗費(fèi)的時(shí)間。
我們可以看到兩次輸出的時(shí)間是不一樣的。受各種影響,解析DNS請(qǐng)求的時(shí)間,服務(wù)器繁忙程序等等。
為什么javascript是一個(gè)事件驅(qū)動(dòng)的語(yǔ)言
javascript圍繞著最初與文檔對(duì)象模型(DOM)相關(guān)的事件架構(gòu)。開(kāi)發(fā)人員可以在事件發(fā)生時(shí)做事情。這些事件有用戶點(diǎn)擊一個(gè)元素,頁(yè)面完成加載等。使用事件,開(kāi)發(fā)人員可以編寫(xiě)事件的監(jiān)聽(tīng)器,當(dāng)事件發(fā)生時(shí)被觸發(fā)。
二,回調(diào)(Callback)
1,什么是回調(diào)
2,剖析回調(diào)
回調(diào)指的是將一個(gè)函數(shù)作為參數(shù)傳遞給另一個(gè)函數(shù),并且通常在第一個(gè)函數(shù)完成后被調(diào)用。
例子:如jquery中的hide()方法,
Js代碼
1,$("p").hide('slow'); 2,$("p").hide('slow',function(){alert("The paragraph is now hidden")});
回調(diào)是可選的,
1就不需要回調(diào)
2,是有回調(diào)的,當(dāng)段落隱藏完成后它就會(huì)被調(diào)用,顯示一個(gè)alert提示。
為了可以看到帶與不帶回調(diào)的代碼之間的區(qū)別
Js代碼
$("p").hide('slow'); alert("The paragraph is now hidden");//1 $("p").hide('slow',function(){alert("The paragraph is now hidden")});//2
1,是沒(méi)有回調(diào),,執(zhí)行順序是一樣但是,我們可以看到p段落還沒(méi)有隱藏完全,alert就出來(lái)
2,是有回調(diào)的,執(zhí)行則是hide完成后在alert
剖析回調(diào)
Js代碼
function haveBreakfast(food,drink,callback){ console.log('Having barakfast of' + food + ', '+ drink); if(callback && typeof(callback) === "function"){ callback(); } } haveBreakfast('foast','coffee',function(){ console.log('Finished breakfast. Time to go to work!'); });
輸出:
Having barakfast of foast,coffee Finished breakfast. Time to go to work!
這里是創(chuàng)建了一個(gè)函數(shù),有三個(gè)參數(shù),第三個(gè)參數(shù)是callback,這個(gè)參數(shù)必須是個(gè)函數(shù)。
haveBreakfast函數(shù)將所吃的東西記錄到控制臺(tái)中然后調(diào)用作為參數(shù)傳遞給它的回調(diào)函數(shù)。
Node.js如何使用回調(diào)
node.js中使用filesystem模塊從磁盤(pán)上讀入文件內(nèi)容的示例
Js代碼
var fs = require('fs'); fs.readFile('somefile.txt','utf8',function(err,data){ if(err) throw err; console.log(data); });
結(jié)果是:somefile.txt里面的內(nèi)容。
1,fs(filesystem)模塊被請(qǐng)求,以便在腳本中使用
2,講文件系統(tǒng)上的文件路徑作為第一個(gè)參數(shù)提供給fs.readFile方法
3,第二個(gè)參數(shù)是utf8,表示文件的編碼
4,將回調(diào)函數(shù)作為第三個(gè)參數(shù)提供給fs.readFile方法
5,回調(diào)函數(shù)的第一個(gè)參數(shù)是err,用于保存在讀取文件時(shí)返回的錯(cuò)誤
6,回調(diào)函數(shù)的第二參數(shù)是打他,用戶保存讀取文件所返回的數(shù)據(jù)。
7,一旦文件被讀取,回調(diào)就會(huì)被調(diào)用
8,如果err為真,那么就會(huì)拋出錯(cuò)誤
9,如果err為假,那么來(lái)自文件的數(shù)據(jù)就可以使用
10,在本例中,數(shù)據(jù)會(huì)記錄到控制臺(tái)上。
再一個(gè),http模塊,http模塊使得開(kāi)發(fā)人員可以創(chuàng)建http客戶端和服務(wù)器。
Js代碼
var http = require('http'); http.get({host:'shapeshed.com'},function(res){ console.log("Got response:" + res.statusCode); }).on('error',function(e){ console.log("Got error:" + e.message); });
結(jié)果:Got response:200
1,請(qǐng)求http模塊,以便在腳本中使用
2,給http.get()方法提供兩個(gè)參數(shù)
3,第一個(gè)參數(shù)是選項(xiàng)對(duì)象。在本示例中,要求獲取shapeshed.com的主頁(yè)
4,第二個(gè)參數(shù)是一個(gè)以響應(yīng)作為參數(shù)的回調(diào)函數(shù)
5,當(dāng)遠(yuǎn)程服務(wù)器返回相應(yīng)時(shí),會(huì)觸發(fā)回調(diào)函數(shù)。
6,在回調(diào)函數(shù)內(nèi)記錄響應(yīng)狀態(tài)碼,如果有錯(cuò)誤的話可以記錄下來(lái)。
接下來(lái),我們看看有4個(gè)不同的I/O操作都在發(fā)生,他們都使用回調(diào)
Js代碼
var fs = require('fs'), http = require('http'); http.get({host:'www.baidu.com'},function(res){ console.log("baidu.com"); }).on('error',function(e){ console.log("Got error:" + e.message); }); fs.readFile('somefile.txt','utf8',function(err,data){ if(err) throw err; console.log("somefile"); }); http.get({host:'www.duokan.com'},function(res){ console.log("duokan.com"); }).on('error',function(e){ console.log("Got error:" + e.message); }); fs.readFile('somefile2.txt','utf8',function(err,data){ if(err) throw err; console.log("somefile2"); });
我們能知道哪個(gè)操作先返回嗎?
猜測(cè)就是從磁盤(pán)上讀取的兩個(gè)文件先返回,因?yàn)闊o(wú)需進(jìn)入網(wǎng)絡(luò),但是我們很難說(shuō)哪個(gè)文件先返回,因?yàn)槲覀儾恢牢募拇笮?。?duì)于兩個(gè)主頁(yè)的獲取,腳本要進(jìn)入網(wǎng)絡(luò),而響應(yīng)時(shí)間則依賴于許多難以預(yù)測(cè)的事情,Node.js進(jìn)程在還有已經(jīng)注冊(cè)的回調(diào)尚未觸發(fā)之前將不會(huì)退出?;卣{(diào)首先解決不可預(yù)測(cè)性的方法,他也是處理并發(fā)(或者說(shuō)一次做超過(guò)一件事情)的高效方法。
下面是我執(zhí)行的結(jié)果
同步和異步代碼
先看代碼,同步(或者阻塞)代碼
Js代碼
function sleep(milliseconds){ var start = new Date().getTime(); while((new Date().getTime() -start) < milliseconds){ } } function fetchPage(){ console.log('fetching page'); sleep(2000); console.log('data returned from requesting page'); } function fetchApi(){ console.log('fetching api'); sleep(2000); console.log('data returned from the api'); } fetchPage(); fetchApi();
當(dāng)腳本運(yùn)行時(shí),fetchPage()函數(shù)會(huì)被調(diào)用,直到它返回之前,腳本的運(yùn)行是被阻塞的,在fetchPage()函數(shù)返回之前,程序是不能移到fetchApi()函數(shù)中的。這稱為阻塞操作。
Node.js幾乎從不使用這種編碼風(fēng)格,而是異步地調(diào)用回調(diào)。
看下下面編碼,,
Js代碼
var http = require('http'); function fetchPage(){ console.log('fetching page'); http.get({host:'www.baidu.com',path:'/?delay=2000'}, function(res){ console.log('data returned from requesting page'); }).on('error',function(e){ console.log("There was an error" + e); }); } function fetchApi(){ console.log('fetching api'); http.get({host:'www.baidu.com',path:'/?delay=2000'}, function(res){ console.log('data returned from requesting api'); }).on('error',function(e){ console.log("There was an error" + e); }); } fetchPage(); fetchApi();
允許這段代碼的時(shí)候,就不再等待fetchPage()函數(shù)返回了,fetchApi()函數(shù)隨之立刻被調(diào)用。代碼通過(guò)使用回調(diào),是非阻塞的了。一旦調(diào)用了,兩個(gè)函數(shù)都會(huì)偵聽(tīng)遠(yuǎn)程服務(wù)器的返回,并以此觸發(fā)回調(diào)函數(shù)。
注意這些函數(shù)的返回順序是無(wú)法保證的,而是和網(wǎng)絡(luò)有關(guān)。
事件循環(huán)
Node.js使用javascript的事件循環(huán)來(lái)支持它所推崇的異步編程風(fēng)格?;旧?,事件循環(huán)使得系統(tǒng)可以將回調(diào)函數(shù)先保存起來(lái),而后當(dāng)事件在將來(lái)發(fā)生時(shí)再運(yùn)行。這可以是數(shù)據(jù)庫(kù)返回?cái)?shù)據(jù),也可以是HTTP請(qǐng)求返回?cái)?shù)據(jù)。因?yàn)榛卣{(diào)函數(shù)的執(zhí)行被推遲到事件反生之后,于是就無(wú)需停止執(zhí)行,控制流可以返回到Node運(yùn)行時(shí)的環(huán)境,從而讓其他事情發(fā)生。
Node.js經(jīng)常被當(dāng)作是一個(gè)網(wǎng)絡(luò)編程框架,因?yàn)樗脑O(shè)計(jì)旨在處理網(wǎng)絡(luò)中數(shù)據(jù)流的不確定性。促成這樣的設(shè)計(jì)的是事件循環(huán)和對(duì)回調(diào)的使用,他們似的程序員可以編寫(xiě)對(duì)網(wǎng)絡(luò)或I/O事件進(jìn)行響應(yīng)的異步代碼。
需要遵循的規(guī)則有:函數(shù)必須快速返回,函數(shù)不得阻塞,長(zhǎng)時(shí)間運(yùn)行的操作必須移到另一個(gè)進(jìn)程中。
Node.js所不適合的地方包括處理大量數(shù)據(jù)或者長(zhǎng)時(shí)間運(yùn)行計(jì)算等。Node.js旨在網(wǎng)絡(luò)中推送數(shù)據(jù)并瞬間完成。
- Node.js中同步和異步編程的區(qū)別及使用方法
- Node.js基礎(chǔ)入門(mén)之回調(diào)函數(shù)及異步與同步詳解
- Nodejs讓異步變成同步的方法
- Node.js模擬發(fā)起http請(qǐng)求從異步轉(zhuǎn)同步的5種用法
- node.js中的forEach()是同步還是異步呢
- 掌握Node.js中的Promise異步編程方式
- async/await與promise(nodejs中的異步操作問(wèn)題)
- NodeJS中利用Promise來(lái)封裝異步函數(shù)
- node異步方法的異步調(diào)用與同步調(diào)用實(shí)現(xiàn)方法示例
相關(guān)文章
Node中文件斷點(diǎn)續(xù)傳原理和方法總結(jié)
在之前做過(guò)一個(gè)小項(xiàng)目,涉及到了文件上傳,在大文件上面使用了斷點(diǎn)續(xù)傳,降低了服務(wù)器方面的壓力,現(xiàn)在小編把Node中文件斷點(diǎn)續(xù)傳原理和方法總結(jié)分享給大家,感興趣的朋友一起看看吧2022-01-01在?node?中使用?koa-multer?庫(kù)上傳文件的方式詳解
本文主要介紹了上傳單個(gè)文件、多個(gè)文件,文件數(shù)量大小限制、限制文件上傳類(lèi)型和對(duì)上傳的圖片進(jìn)行不同大小的裁剪,對(duì)node使用?koa-multer?庫(kù)上傳文件相關(guān)知識(shí)感興趣的朋友一起看看吧2024-01-01詳解nodejs 開(kāi)發(fā)企業(yè)微信第三方應(yīng)用入門(mén)教程
這篇文章主要介紹了詳解nodejs 開(kāi)發(fā)企業(yè)微信第三方應(yīng)用入門(mén)教程,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2019-03-03node.js中的fs.lchown方法使用說(shuō)明
這篇文章主要介紹了node.js中的fs.lchown方法使用說(shuō)明,本文介紹了fs.lchown的方法說(shuō)明、語(yǔ)法、接收參數(shù)、使用實(shí)例和實(shí)現(xiàn)源碼,需要的朋友可以參考下2014-12-12Nodejs回調(diào)加超時(shí)限制兩種實(shí)現(xiàn)方法
這篇文章主要介紹了Nodejs回調(diào)加超時(shí)限制兩種實(shí)現(xiàn)方法的相關(guān)資料,需要的朋友可以參考下2017-06-06