最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Node.js中的事件驅(qū)動(dòng)編程詳解

 更新時(shí)間:2014年08月16日 12:04:06   作者:Jack Yao  
這篇文章主要介紹了Node.js中的事件驅(qū)動(dòng)編程詳解,本文主要講解理論性知識(shí),如什么是事件驅(qū)動(dòng)編程、什么是閉包、閉包如何幫助異步編程等知識(shí),需要的朋友可以參考下

在傳統(tǒng)程編程模里,I/O操作就像一個(gè)普通的本地函數(shù)調(diào)用:在函數(shù)執(zhí)行完之前程序被堵塞,無(wú)法繼續(xù)運(yùn)行。堵塞I/O起源于早先的時(shí)間片模型,這種模型下每個(gè)進(jìn)程就像一個(gè)獨(dú)立的人,目的是將每個(gè)人區(qū)分開(kāi),而且每個(gè)人在同一時(shí)刻通常只能做一件事,必須等待前面的事做完才能決定下一件事做什么。但是這種在計(jì)算機(jī)網(wǎng)絡(luò)和Internet上被廣泛使用的“一個(gè)用戶,一個(gè)進(jìn)程”的模型伸縮性很差。管理多個(gè)進(jìn)程時(shí),會(huì)耗費(fèi)很多內(nèi)存,上下文切換也會(huì)占用大量資源,這些對(duì)操作系統(tǒng)是個(gè)很大的負(fù)擔(dān),而且隨著進(jìn)程數(shù)的遞增,會(huì)導(dǎo)致系統(tǒng)性能急劇衰減。

多線程是個(gè)替代方案,線程是一個(gè)輕量級(jí)的進(jìn)程,它會(huì)和同一個(gè)進(jìn)程內(nèi)的其它線程共享內(nèi)存,它更像傳統(tǒng)模型的擴(kuò)展,用來(lái)并發(fā)執(zhí)行多個(gè)線程,當(dāng)一個(gè)線程等待I/O操作時(shí),其它線程可以接管CPU,當(dāng)I/O操作完成,前面等待的線程會(huì)被喚醒。就是說(shuō),一個(gè)運(yùn)行中的線程可以被中斷,然后稍候再被恢復(fù)。此外,在一些系統(tǒng)下線程可以在多核CPU的不同核心下并行運(yùn)行。

程序員并不知道線程會(huì)在什么具體時(shí)間運(yùn)行,他們必須很小心的處理共享內(nèi)存的并發(fā)訪問(wèn),因此必須使用一些同步原語(yǔ)來(lái)同步訪問(wèn)某個(gè)數(shù)據(jù)結(jié)構(gòu),比如使用鎖或信號(hào)量,以此來(lái)強(qiáng)制線程以特定的行為和計(jì)劃執(zhí)行。那些大量依賴線程間的共享狀態(tài)的應(yīng)用程序,很容易就會(huì)出現(xiàn)一些隨機(jī)性很強(qiáng),難以查找的奇怪問(wèn)題。

還有一種方式是使用多線程協(xié)作,由你自己負(fù)責(zé)顯式的釋放CPU,并把CPU時(shí)間交給其他線程使用,因?yàn)橛赡阌H自來(lái)控制線程的執(zhí)行計(jì)劃,因此減小了對(duì)同步的需求,但是也提高了程序的復(fù)雜度和出錯(cuò)的機(jī)會(huì),而且并沒(méi)有避免多線程的那些問(wèn)題。

什么是事件驅(qū)動(dòng)編程

事件驅(qū)動(dòng)編程(Evnet-driven programming)是一種編程風(fēng)格,由事件來(lái)決定程序的執(zhí)行流程,事件由事件處理器(event handler)或事件回調(diào)(event callback)來(lái)處理,事件回調(diào)是當(dāng)某個(gè)特定事件發(fā)生時(shí)被調(diào)用的函數(shù),比如數(shù)據(jù)庫(kù)返回了查詢結(jié)果或者用戶單擊了一個(gè)按鈕。

回想下,在傳統(tǒng)的堵塞I/O編程模式里,數(shù)據(jù)庫(kù)查詢可能像這樣:

復(fù)制代碼 代碼如下:

result = query('SELECT * FROM posts WHERE id = 1');

do_something_with(result);


上面的query函數(shù)會(huì)讓當(dāng)前線程或進(jìn)程一直處于等待狀態(tài),直到底層數(shù)據(jù)庫(kù)完成查詢操作并返回。

在事件驅(qū)動(dòng)模型里,這個(gè)查詢會(huì)變成這樣:

復(fù)制代碼 代碼如下:

query_finished = function(result) {

        do_something_with(result);

}

query('SELECT * FROM posts WHERE id = 1', query_finished);

   首先你定義了一個(gè)叫query_finished的函數(shù),它包含了查詢完成后要做的事。然后把這個(gè)函數(shù)當(dāng)做參數(shù)傳遞給query函數(shù),當(dāng)query執(zhí)行完畢會(huì)調(diào)用query_finished,而不是僅僅返回查詢結(jié)果。

當(dāng)你感興趣的事件發(fā)生時(shí)會(huì)調(diào)用你定義的函數(shù),而不是簡(jiǎn)單的返回結(jié)果值,這種編程模型就叫事件驅(qū)動(dòng)編程或異步編程。這是Node一個(gè)最明顯的特性,這種編程模型意味著當(dāng)前進(jìn)程在執(zhí)行I/O操作時(shí)不會(huì)被阻塞,因此,多個(gè)I/O操作可以并行執(zhí)行,當(dāng)操作完成后相應(yīng)的回調(diào)函數(shù)就會(huì)被調(diào)用。

事件驅(qū)動(dòng)編程底層依賴于事件循環(huán)(event loop),事件循環(huán)基本上是事件檢測(cè)和事件處理器觸發(fā)這兩種函數(shù)不斷循環(huán)調(diào)用的一個(gè)結(jié)構(gòu)。在每次循環(huán)里,事件循環(huán)機(jī)制需要檢測(cè)發(fā)生了哪些事件,當(dāng)事件發(fā)生時(shí),它找到對(duì)應(yīng)的回調(diào)函數(shù)并調(diào)用它。

事件循環(huán)只是運(yùn)行在進(jìn)程內(nèi)的一個(gè)線程,當(dāng)事件發(fā)生時(shí),事件處理器可以單獨(dú)運(yùn)行并且不會(huì)被中斷,也就是說(shuō):

1.在某個(gè)特定時(shí)刻最多有一個(gè)事件回調(diào)函數(shù)運(yùn)行
2.任何事件處理器運(yùn)行時(shí)都不會(huì)被中斷

有了這個(gè),開(kāi)發(fā)人員就可以不再為線程同步和并發(fā)修改共享內(nèi)存這些事頭疼了。

一個(gè)眾所周知的秘密:

很久以前,系統(tǒng)編程社區(qū)的人們就知道事件驅(qū)動(dòng)編程是創(chuàng)建高并發(fā)服務(wù)最佳方式,因?yàn)樗挥帽4婧芏嗌舷挛?,因此?jié)省了大量?jī)?nèi)存,也沒(méi)有那么多上下文切換,又節(jié)省了大量執(zhí)行時(shí)間。

慢慢的,這種理念滲透到了其他的平臺(tái)和社區(qū),出現(xiàn)了一些有名的事件循環(huán)實(shí)現(xiàn),比如Ruby的Event machine,Perl的AnyEvnet,以及Python的Twisted,除了這些還有很多其它的實(shí)現(xiàn)和語(yǔ)言。

用這些框架做開(kāi)發(fā),需要學(xué)習(xí)框架相關(guān)的特定知識(shí)以及框架特定的類庫(kù),比如,使用Event Machine時(shí),為了享受非阻塞帶來(lái)的好處,你得避免使用同步類庫(kù),只能用Event Machine的異步類庫(kù)。如果你使用了任何阻塞類庫(kù)(比如Ruby的大多數(shù)標(biāo)準(zhǔn)庫(kù)),你的服務(wù)器就失去了最佳的伸縮性,因?yàn)槭录h(huán)依然會(huì)不斷地被阻塞,時(shí)不時(shí)地阻礙了I/O事件的處理。

Node最初就被設(shè)計(jì)成一個(gè)非阻塞I/O服務(wù)器平臺(tái),因此一般情況下,你應(yīng)該期望運(yùn)行在它上面的所有代碼都是非阻塞的。因?yàn)镴avaScript非常小,而且它不強(qiáng)制使用任何I/O模型(因?yàn)樗鼪](méi)有標(biāo)準(zhǔn)的I/O類庫(kù)),因此Node建立在一個(gè)很純凈的環(huán)境里,不會(huì)有什么歷史遺留問(wèn)題。

Node和JavaScript如何簡(jiǎn)化了異步應(yīng)用程序

Node的作者Ryan Dahl,最初使用C來(lái)開(kāi)發(fā)這個(gè)項(xiàng)目,但是發(fā)現(xiàn)維護(hù)函數(shù)調(diào)用的上下文太復(fù)雜,導(dǎo)致代碼復(fù)雜度很高。然后他轉(zhuǎn)用Lua,但是Lua已經(jīng)有個(gè)幾個(gè)阻塞的I/O類庫(kù),阻塞和非阻塞混在一起可能會(huì)讓開(kāi)發(fā)人員很迷惑并因此阻礙了很多人構(gòu)建可伸縮的應(yīng)用,于是Lua也被Dahl拋棄了。最后他轉(zhuǎn)向了JavaScript,JavaScript中的閉包及第一級(jí)對(duì)象的函數(shù),這些特性使JavaScript非常適合用作事件驅(qū)動(dòng)編程。JavaScript的魔力是讓Node如此流行的一個(gè)主要原因。

什么是閉包

閉包可以理解為一個(gè)特殊的函數(shù),但是它可以繼承并訪問(wèn)它自身被定義的那個(gè)作用域里的變量。當(dāng)你將一個(gè)回調(diào)函數(shù)作為參數(shù)傳遞給另外一個(gè)函數(shù)時(shí),它稍候會(huì)被調(diào)用,神奇的是,這個(gè)回調(diào)函數(shù)被稍候調(diào)用時(shí),它居然記住了它自身定義所在的那個(gè)上下文以及父上下文里的變量,而且還可以正常訪問(wèn)它們。這個(gè)強(qiáng)大的特性是Node成功的核心。

下面的例子將展示在Web瀏覽器里JavaScript閉包是如何工作的。假如,你要監(jiān)聽(tīng)一個(gè)按鈕的單機(jī)事件,你可以這樣做:

復(fù)制代碼 代碼如下:

var clickCount = 0;

document.getElementById('myButton').onclick = function() {

        clickCount += 1;

        alert("clicked " + clickCount + " times.");

};

使用jQuery時(shí)是這樣:

復(fù)制代碼 代碼如下:

var clickCount = 0;

$('button#mybutton').click(function() {

        clickedCount ++;

        alert('Clicked ' + clickCount + ' times.');

});

JavaScript里,函數(shù)是第一類對(duì)象,就是說(shuō)你可以把函數(shù)當(dāng)作參數(shù)來(lái)傳遞給其他函數(shù)。上面的兩個(gè)例子,前者把一個(gè)函數(shù)賦值給另一個(gè)函數(shù),后者把函數(shù)作為參數(shù)傳遞給另一個(gè)函數(shù),單擊事件的處理函數(shù)(回調(diào)函數(shù))可以訪問(wèn)函數(shù)定義所在代碼塊下的每個(gè)變量,在這個(gè)例子里,它可以訪問(wèn)在它父閉包內(nèi)定義的clickCount變量。

clickCount變量處在全局作用域(JavaScript里最外層的作用域),它保存了用戶點(diǎn)擊按鈕的次數(shù),通常在全局作用域下存儲(chǔ)變量是個(gè)壞習(xí)慣,因?yàn)槟菢雍苋菀赘渌a沖突,你應(yīng)該把變量放在使用它們的本地作用域里。大多時(shí)候,只用把代碼用一個(gè)函數(shù)包裝起來(lái),等于另外創(chuàng)建了閉包,這樣就可以很容易避免污染全局環(huán)境,就像這樣:

復(fù)制代碼 代碼如下:

                  (function() {

                            var clickCount = 0;

                            $('button#mybutton').click(function() {

                                     clickCount ++;

                                     alert('Clicked ' + clickCount + ' times.');

                            });

                   }());


  注意:上面代碼的第七行,定義了一個(gè)函數(shù)后立刻調(diào)用它,這是JavaScript里一個(gè)常見(jiàn)的設(shè)計(jì)模式:通過(guò)創(chuàng)建函數(shù)來(lái)創(chuàng)建一個(gè)新的作用域。

閉包如何幫助異步編程

在事件驅(qū)動(dòng)編程模型里,先編寫(xiě)事件發(fā)生后將要運(yùn)行的代碼,然后把這些代碼放到一個(gè)函數(shù)里,最后把這個(gè)函數(shù)當(dāng)作參數(shù)傳遞給調(diào)用者,稍后由調(diào)用者函數(shù)調(diào)用。

在JavaScript里,一個(gè)函數(shù)并不是個(gè)孤立的定義,它同時(shí)會(huì)記住自己被聲明的那個(gè)作用域的上下文,這種機(jī)制讓JavaScript的函數(shù)可以訪問(wèn)函數(shù)定義所在那個(gè)上下文及父上下文里的所有變量。

當(dāng)你把一個(gè)回調(diào)函數(shù)當(dāng)作參數(shù)傳遞給調(diào)用者后,這個(gè)函數(shù)就會(huì)在稍后的某個(gè)時(shí)刻被調(diào)用。即使定義回調(diào)函數(shù)的那個(gè)作用域已經(jīng)結(jié)束,在回調(diào)函數(shù)被調(diào)用時(shí),它依然能夠訪問(wèn)這個(gè)已結(jié)束的作用域及其父作用域里的所有變量。像最后那個(gè)例子,回調(diào)函數(shù)在jQuery的click()內(nèi)部被調(diào)用,它卻依然能訪問(wèn)clickCount變量。

前面展現(xiàn)了閉包的神奇之處,把狀態(tài)變量傳遞給一個(gè)函數(shù)就可以讓你不用維護(hù)狀態(tài)就能進(jìn)行事件驅(qū)動(dòng)編程,JavaScript的閉包機(jī)制會(huì)幫你維護(hù)它們。

小結(jié)

事件驅(qū)動(dòng)編程是一種通過(guò)事件觸發(fā)來(lái)決定程序執(zhí)行流程的編程模型。程序員為他們感興趣的事件注冊(cè)回調(diào)函數(shù)(通常被稱作事件處理器),然后系統(tǒng)在事件發(fā)生時(shí)調(diào)用已注冊(cè)的事件處理器。這種編程模型有很多傳統(tǒng)阻塞編程模型所不具備的優(yōu)勢(shì),以前要實(shí)現(xiàn)類似的特性,就必須使用多進(jìn)程/多線程才行。

JavaScript是種強(qiáng)大的語(yǔ)言,因?yàn)樗牡谝活愋蛯?duì)象的函數(shù)和閉包特性,讓它很適合事件驅(qū)動(dòng)編程。

相關(guān)文章

  • 在Node.js中使用HTTP上傳文件的方法

    在Node.js中使用HTTP上傳文件的方法

    這篇文章主要介紹了在Node.js中使用HTTP上傳文件的方法,作者以windows下的visual studio作為操作node的環(huán)境,推薦閱讀!需要的朋友可以參考下
    2015-06-06
  • node.js利用express自動(dòng)搭建項(xiàng)目的全過(guò)程

    node.js利用express自動(dòng)搭建項(xiàng)目的全過(guò)程

    這篇文章主要給大家介紹了關(guān)于node.js利用express自動(dòng)搭建項(xiàng)目的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2021-04-04
  • 用nodejs實(shí)現(xiàn)json和jsonp服務(wù)的方法

    用nodejs實(shí)現(xiàn)json和jsonp服務(wù)的方法

    本篇文章主要介紹了用nodejs實(shí)現(xiàn)json和jsonp服務(wù)的方法,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2017-08-08
  • Nodejs學(xué)習(xí)筆記之測(cè)試驅(qū)動(dòng)

    Nodejs學(xué)習(xí)筆記之測(cè)試驅(qū)動(dòng)

    本文是本系列文章的第二篇,主要是測(cè)試針對(duì)于web后端的驅(qū)動(dòng),在開(kāi)發(fā)過(guò)程中,在開(kāi)發(fā)完成一段代碼后如果負(fù)責(zé)任而不是說(shuō)完全把問(wèn)題交給測(cè)試人員去發(fā)現(xiàn)的話,這個(gè)時(shí)候通常都會(huì)去做一些手動(dòng)的測(cè)試。
    2015-04-04
  • nodejs高大上的部署方式(PM2)

    nodejs高大上的部署方式(PM2)

    這篇文章主要介紹了nodejs高大上的部署方式(PM2) ,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2018-09-09
  • Node.js的路由、EJS模板引擎、GET和POST請(qǐng)求講解

    Node.js的路由、EJS模板引擎、GET和POST請(qǐng)求講解

    這篇文章介紹了Node.js的路由、EJS模板引擎、GET和POST請(qǐng)求,對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2022-06-06
  • Node Sass依賴問(wèn)題排查思路解析

    Node Sass依賴問(wèn)題排查思路解析

    這篇文章主要為大家介紹了Node Sass依賴問(wèn)題排查思路解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-04-04
  • nodejs實(shí)現(xiàn)獲取本地文件夾下圖片信息功能示例

    nodejs實(shí)現(xiàn)獲取本地文件夾下圖片信息功能示例

    這篇文章主要介紹了nodejs實(shí)現(xiàn)獲取本地文件夾下圖片信息功能,涉及node.js針對(duì)文件、目錄的遍歷、讀取等相關(guān)操作技巧,需要的朋友可以參考下
    2019-06-06
  • nodejs讀寫(xiě)json文件的簡(jiǎn)單方法(必看)

    nodejs讀寫(xiě)json文件的簡(jiǎn)單方法(必看)

    下面小編就為大家?guī)?lái)一篇nodejs讀寫(xiě)json文件的簡(jiǎn)單方法(必看)。
    2017-03-03
  • 利用adb shell和node.js實(shí)現(xiàn)抖音搶紅包功能(推薦)

    利用adb shell和node.js實(shí)現(xiàn)抖音搶紅包功能(推薦)

    這篇文章主要介紹了利用adb shell和node.js實(shí)現(xiàn)抖音搶紅包功能,非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友可以參考下
    2018-02-02

最新評(píng)論

崇左市| 萨嘎县| 磐安县| 麻栗坡县| 阜城县| 邵阳县| 绵阳市| 巴中市| 平谷区| 平遥县| 无棣县| 洛扎县| 云龙县| 涞水县| 噶尔县| 阳原县| 麟游县| 巴彦淖尔市| 田林县| 沅陵县| 凌云县| 湘西| 资阳市| 洪江市| 湟中县| 保山市| 南宁市| 盈江县| 梨树县| 靖安县| 青州市| 香格里拉县| 侯马市| 浦县| 康定县| 汉寿县| 万载县| 岑巩县| 白山市| 容城县| 揭西县|