999精品在线视频,手机成人午夜在线视频,久久不卡国产精品无码,中日无码在线观看,成人av手机在线观看,日韩精品亚洲一区中文字幕,亚洲av无码人妻,四虎国产在线观看 ?

Node.js異步編程模式探討

2018-10-18 08:38:28周安輝

周安輝

(內(nèi)江職業(yè)技術(shù)學(xué)院,四川 內(nèi)江 641100)

0 引言

Node.js是一個編寫網(wǎng)絡(luò)服務(wù)和網(wǎng)頁應(yīng)用的平臺,采用C++語言編寫,優(yōu)化了Google V8引擎,能夠高效地運(yùn)行JavaScript代碼,同時提供了文件、網(wǎng)絡(luò)等眾多系統(tǒng)級的API,有助于開發(fā)人員快速地構(gòu)建高性能的網(wǎng)絡(luò)服務(wù)及其應(yīng)用。

Node.js圍繞一個事件驅(qū)動的無阻塞I/O的異步編程模式而構(gòu)建,代碼執(zhí)行無須阻塞等待某種低速的I/O操作完成而繼續(xù),充分利用了有限的資源,非常適合編寫處理大量并發(fā)請求的后臺網(wǎng)絡(luò)服務(wù)。此外,服務(wù)器端與客戶端的編寫,統(tǒng)一使用JavaScript語言,受到開發(fā)人員的極大歡迎。

1 Node.js編程中的函數(shù)概念[2]

基于函數(shù)的傳統(tǒng)編程,開發(fā)人員是相當(dāng)熟悉的,大部分編程語言都使用,Node.js也不例外,但是要注意一些概念上的區(qū)別。在Node.js的編程中,要正確理解以下幾個基本的函數(shù)概念,可以幫助我們掌握Node.js的編程模式。

1.1 立即執(zhí)行函數(shù)

在Node.js中可以在定義一個函數(shù)后立即執(zhí)行它。只需要簡單地用()括號包裹函數(shù),并調(diào)用它,如下所示:.

(function myData(){

console.log('myData was executed!');

})();

在 JavaScript中,if、else或 while語句體并不會創(chuàng)建一個新的變量作用域。如下所示:

var myData=123;

if(true){

var myData=456;

}

console.log(myData);//456;

在JavaScript中,只有使用一個立即執(zhí)行函數(shù)會創(chuàng)建一個新的變量作用域。如下所示:

var myData=123;

if(true){

(function(){//create a new scope

var myData=456;

console.log(myData);//456;

})();

}

console.log(myData);//123;

1.2 匿名函數(shù)

一個沒有名字的函數(shù)被稱為匿名函數(shù)。在JavaScript中,你可以指派一個函數(shù)給一個變量。如果你打算把一個函數(shù)賦值給一個變量,你不需要使用命名函數(shù)。

以下兩種方式定義一個內(nèi)聯(lián)函數(shù),兩者是等價的:

var foo1=function namedFunction(){

console.log('foo1');

}/*www.java2s.com*/

foo1();//foo1

var foo2=function(){//no function name i.e.anonymous function

console.log('foo2');

}

foo2();//foo2

1.3 首類函數(shù)

JavaScript語言擁有首類函數(shù)。首類函數(shù)意味著函數(shù)被當(dāng)作對象一樣的東西來看待,可以把它指派給一個變量。

高階函數(shù)

因?yàn)镴avaScript語言允許指派函給變量,所以能夠傳送函數(shù)給其他函數(shù)。高階函數(shù)意味著使用其他函數(shù)作參數(shù)或者返回一個函數(shù)作結(jié)果。setTimeout是一個很常見的高階函數(shù)例子,用法如下:.

setTimeout(function(){

console.log('2000 milliseconds have passed since this demo started');

},2000);

在Node.js運(yùn)行這個代碼,你會在兩秒后才看見控制臺日志消息。

在setTimeout中使用了一個匿名函數(shù)作為第一個參數(shù),這讓setTimeout成為一個高階函數(shù)。

也可定義一個函數(shù),顯式傳遞給setTimeout,如下所示:

function foo(){

console.log('2000 milliseconds have passed since this demo started');

}

setTimeout(foo,2000);

1.4 閉包函數(shù)

這個概念是非常直觀和簡單。如果一個函數(shù)定義在另外一個函數(shù)的內(nèi)部,內(nèi)部函數(shù)要訪問外部函數(shù)聲明的變量。如下所示:

function outerFunction(arg){

var variableInOuterFunction=arg;

function myValue(){

console.log(variableInOuterFunction);

}

myValue();

}

outerFunction ('hello closure!');//logs hello closure!

即使外部函數(shù)已經(jīng)返回,內(nèi)部函數(shù)還是能夠訪問外部作用域的變量。因?yàn)樵撟兞咳匀槐粌?nèi)部函數(shù)綁定,并不依賴于外部函數(shù)。如下所示:

function outerFunction(arg){

var variableInOuterFunction=arg;return function(){

console.lo(variableInOuterFunction);

}

}

var innerFunction = outerFunction('hello closure!');

innerFunction();

2 Node.js的標(biāo)準(zhǔn)回調(diào)編程模式

Node.js異步編程采用后續(xù)傳遞風(fēng)格(continuation-passing style,CPS),編寫的CPS函數(shù)有一個顯式的“后續(xù)”函數(shù)作為額外參數(shù),在調(diào)用CPS函數(shù)計(jì)算出返回值時,并不表示函數(shù)結(jié)束,而將CPS函數(shù)的返回值作為“后續(xù)”函數(shù)的參數(shù),繼續(xù)調(diào)用“后續(xù)”函數(shù),顯示地將流程控制權(quán)傳遞給“后續(xù)”函數(shù)。

2.1 回調(diào)函數(shù)

在后續(xù)傳遞風(fēng)格的編程中,每個函數(shù)在執(zhí)行完畢后都會調(diào)用一個回調(diào)函數(shù),將程序繼續(xù)進(jìn)行下去。如你所見,在JavaScript就是采用這種方式編程,例如Node.js中,將input.txt文件加載到內(nèi)存并顯示出的例子:

var fs=require('fs');

fs.readFile ('./input.txt',function(err,data){

if(err){

console.log(err.stack);

return; }

console.log(' 文件內(nèi)容: ',data.toString());

});

console.log('Reading file...');

執(zhí)行這段代碼,首先會顯示'Reading file...'字符串,然后等待回調(diào)函數(shù)的結(jié)果返回后,才會顯示文件內(nèi)容,這是一種典型的異步執(zhí)行模式。

注意:內(nèi)聯(lián)匿名回調(diào)函數(shù)的第一個參數(shù)是一個錯誤對象,如果有錯誤發(fā)生,其為Error類的一個實(shí)例,這是Node.js中應(yīng)用CPS編程的一個通用模式。

2.2 鏈?zhǔn)交卣{(diào)函數(shù)

使用異步方法并不能保證執(zhí)行次序,下面的例子是我們經(jīng)常犯的錯誤:

var fs=require('fs');

fs.rename('/tmp/hello','/tmp/world',(err)=>{

if(err)throw err;

console.log('renamed complete');

});

fs.stat('/tmp/world',(err,stats)=>{

if(err)throw err;

console.log(`stats:${JSON.stringify(stats)}`);

});

fs.stat?可能在fs.rename之前被執(zhí)行。要保證流程控制權(quán)的正確執(zhí)行次序,正確的做法是采用鏈?zhǔn)交卣{(diào)函數(shù),如下所示:

var fs=require('fs');

fs.rename('/tmp/hello','/tmp/world',(err)=>{

if(err)throw err;

fs.stat('/tmp/world',(err,stats)=>{

if(err)throw err;

console.log(`stats:${JSON.stringify(stats)}`);

});

});

3 事件驅(qū)動編程模式

Node.js大量使用事件來決定程序的流程控制權(quán),使它與其他采用“事件驅(qū)動編程”相似技術(shù)相比較,Node.js就顯得更快更高效。Node.js一旦啟動了它的服務(wù)器,它僅是簡單地初始變量,聲明函數(shù),然后就只需等待事件發(fā)生。

標(biāo)準(zhǔn)回調(diào)模式是單事件工作模式,在異步函數(shù)返回其結(jié)果時觸發(fā)調(diào)用回調(diào)函數(shù)。如果是在函數(shù)的執(zhí)行中發(fā)生了多個事件或事件重復(fù)發(fā)生,這種模式就不是很理想了,而事件驅(qū)動模式則在這種情形下很好工作。一般而言,在需要請求的操作完成后要重獲流程控制權(quán),采用標(biāo)準(zhǔn)回調(diào)模式,而當(dāng)多個事件發(fā)生或事件重復(fù)發(fā)生時,要決定流程控制權(quán),采用事件驅(qū)動模式。本質(zhì)上,可以把Node.js標(biāo)準(zhǔn)回調(diào)模式視為特定的單事件驅(qū)動編程模式。

在事件驅(qū)動模式編程中,偵聽事件的函數(shù)充當(dāng)觀察器,只有事件發(fā)生器發(fā)射一個事件被觀察到時,它的偵聽器的回調(diào)函數(shù)才開始執(zhí)行。

3.1 事件發(fā)生器內(nèi)置事件類型的偵聽

下面的代碼,create_websever.js用于創(chuàng)建一個web服務(wù)器,ex2_event.js演示請求web頁面時,并對response發(fā)射的data與end內(nèi)置事件類型進(jìn)行響應(yīng):

create_websever.js文件如下所示:

const http=require('http');

const hostname='127.0.0.1';

const port=3000;

const server=http.createServer((req,res)=>{res.statusCode=200;

res.setHeader('Content-Type','text/plain');

res.end('Hello World ');

});

server.listen(port,hostname,()=>{

console.log(`Server running at http://${hostname}:${port}/`);

});

ex2_event.js文件:

var http=require('http');

var options={

host:'127.0.0.1',

port:3000,

path:'/'

};

var req=http.request(options,function(res){res.setEncoding('utf8');

res.on('data',function(data){console.log('some data from the response',data);

});

res.on('end',function(){console.log('response ended');

});

})

req.end();

3.2 自定義事件類型的偵聽[1]

Node.js?使用events模塊和?EventEmitter?類實(shí)現(xiàn)自定義事件類型編程。通過?EventEmitter?類來綁定事件與事件偵聽器,可以實(shí)現(xiàn)多個自定義事件類型的發(fā)射和偵聽。如下代碼所示:

//Import events module

var events=require('events');

//Create an eventEmitter object

var eventEmitter=newevents.EventEmitter();

//Create an event handler as follows

var connectHandler=function connected(){console.log('connection succesful.');

//Fire the data_received event

eventEmitter.emit('data_received');}

//Bind the connection event with the handler

eventEmitter.on('connection',connectHandler);

//Bind the data_received event with the anonymous function

eventEmitter.on ('data_received',function(){

console.log('data received succesfully.');});

//Fire the connection event

eventEmitter.emit('connection');

console.log(“Program Ended.”);

3.3 對“error”事件的處理

在Node中,事件發(fā)生器采用通用接口服務(wù)各種類型的事件,但是“error”事件除外,大部分Node事件發(fā)生器在程序產(chǎn)生錯誤時都要產(chǎn)生“error”事件。如果不監(jiān)聽該事件,“error”事件發(fā)生時,Node事件發(fā)生器會拋出一個未捕獲的異常,顯示一個堆棧追蹤,而且Node進(jìn)程會退出。

最佳實(shí)踐是始終偵聽“error”事件,如下所示:

var myEmitter=new(require('events').EventEmitter)();

myEmitter.on('error',(err)=>{

console.log('whoops!there was an error');

});

myEmitter.emit ('error', new Error('whoops!'));

4 理解Node.js事件輪詢[5]event loop

Node.js的event loop,后臺采用Libuv[4]高性能的事件輪詢模型,負(fù)責(zé)調(diào)度回調(diào)函數(shù)隊(duì)列的執(zhí)行,是實(shí)現(xiàn)非阻塞I/O異步編程的關(guān)鍵機(jī)制。當(dāng)Node.js啟動時,將初始化event loop,處理那些可能做異步API調(diào)用、定制計(jì)時器或調(diào)用process.nextTick()的輸入腳本,然后開始處理event loop。

event loop包括六個循環(huán)階段,如下圖1所示:

圖1 六個循環(huán)階

每個階段都有一個可執(zhí)行的回調(diào)函數(shù)的FIFO隊(duì)列。盡管每個階段具有自己特殊方式,通常,當(dāng)事件循環(huán)進(jìn)入一個給定的階段,它將執(zhí)行這個階段的任何特定操作,然后執(zhí)行在這個階段的隊(duì)列中的回調(diào)函數(shù),直到隊(duì)列為空或者回調(diào)函數(shù)數(shù)量達(dá)到上限,event loop會進(jìn)入到下一下階段,等等,細(xì)節(jié)可參考官方文檔[3]。

5 小結(jié)

Node.js后續(xù)傳遞風(fēng)格的編程,看上去很丑陋,并且與傳統(tǒng)的編程思維模式相違背,讓人入手時難以適應(yīng),只有當(dāng)你深入理解事件輪詢event loop的基本原理后,對于Node.js后續(xù)傳遞風(fēng)格的異步編程會有較大幫助,并且會逐步喜歡上它的簡明與高效。

主站蜘蛛池模板: 深爱婷婷激情网| 国产精品一线天| 国产流白浆视频| 亚洲成AV人手机在线观看网站| 美女内射视频WWW网站午夜| 尤物成AV人片在线观看| 永久免费无码日韩视频| 婷婷激情亚洲| 小说区 亚洲 自拍 另类| 亚洲美女AV免费一区| 十八禁美女裸体网站| 国产黑丝一区| 亚洲综合九九| 香蕉久久国产精品免| 国产精品毛片一区视频播| 久久综合色88| 精品久久高清| 日韩大片免费观看视频播放| 狠狠色丁香婷婷综合| 国产成+人+综合+亚洲欧美 | 三上悠亚在线精品二区| 国产在线专区| 五月婷婷综合网| 无码综合天天久久综合网| 99在线视频网站| 国产特一级毛片| 日本高清视频在线www色| 看你懂的巨臀中文字幕一区二区| 欧美日韩在线第一页| 亚洲二区视频| 午夜欧美在线| 波多野结衣第一页| 五月婷婷激情四射| 97精品久久久大香线焦| 亚洲人成网站在线播放2019| 久久综合九色综合97网| 国内老司机精品视频在线播出| 午夜在线不卡| 99视频在线免费观看| 99久久精品免费看国产免费软件 | 大香伊人久久| 免费无遮挡AV| 99精品在线视频观看| 亚洲精品卡2卡3卡4卡5卡区| 日本高清在线看免费观看| 日韩中文无码av超清| 另类综合视频| 日韩区欧美国产区在线观看| 国产免费福利网站| 久久精品娱乐亚洲领先| 国内丰满少妇猛烈精品播 | 亚洲a级在线观看| 多人乱p欧美在线观看| 国产日韩欧美视频| 香蕉久人久人青草青草| 激情成人综合网| 国产成人艳妇AA视频在线| 欧美中文字幕在线视频| 国产夜色视频| 成人精品免费视频| 成人国内精品久久久久影院| 精品久久久久久中文字幕女| 天天干伊人| 婷婷丁香在线观看| 福利在线不卡一区| 免费国产一级 片内射老| 114级毛片免费观看| 国产成人三级| 国产亚洲精久久久久久无码AV| 欧美激情综合| 免费播放毛片| 2018日日摸夜夜添狠狠躁| 国产香蕉国产精品偷在线观看| 精品伊人久久大香线蕉网站| 亚洲国产无码有码| 色视频国产| 亚洲专区一区二区在线观看| 国产精品午夜福利麻豆| 国产精品无码一区二区桃花视频| 天天色综网| 亚洲第一区在线| 国产91全国探花系列在线播放|