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

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

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

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

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

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

還有一種方式是使用多線程協(xié)作,由你自己負(fù)責(zé)顯式的釋放CPU,并把CPU時間交給其他線程使用,因為由你親自來控制線程的執(zhí)行計劃,因此減小了對同步的需求,但是也提高了程序的復(fù)雜度和出錯的機會,而且并沒有避免多線程的那些問題。

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

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

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

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

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

do_something_with(result);


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

在事件驅(qū)動模型里,這個查詢會變成這樣:

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

query_finished = function(result) {

        do_something_with(result);

}

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

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

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

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

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

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

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

一個眾所周知的秘密:

很久以前,系統(tǒng)編程社區(qū)的人們就知道事件驅(qū)動編程是創(chuàng)建高并發(fā)服務(wù)最佳方式,因為它不用保存很多上下文,因此節(jié)省了大量內(nèi)存,也沒有那么多上下文切換,又節(jié)省了大量執(zhí)行時間。

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

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

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

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

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

什么是閉包

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

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

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

var clickCount = 0;

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

        clickCount += 1;

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

};

使用jQuery時是這樣:

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

var clickCount = 0;

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

        clickedCount ++;

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

});

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

clickCount變量處在全局作用域(JavaScript里最外層的作用域),它保存了用戶點擊按鈕的次數(shù),通常在全局作用域下存儲變量是個壞習(xí)慣,因為那樣很容易跟其他代碼沖突,你應(yīng)該把變量放在使用它們的本地作用域里。大多時候,只用把代碼用一個函數(shù)包裝起來,等于另外創(chuàng)建了閉包,這樣就可以很容易避免污染全局環(huán)境,就像這樣:

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

                  (function() {

                            var clickCount = 0;

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

                                     clickCount ++;

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

                            });

                   }());


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

閉包如何幫助異步編程

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

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

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

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

小結(jié)

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

JavaScript是種強大的語言,因為它的第一類型對象的函數(shù)和閉包特性,讓它很適合事件驅(qū)動編程。

相關(guān)文章

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

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

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

    node.js利用express自動搭建項目的全過程

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

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

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

    Nodejs學(xué)習(xí)筆記之測試驅(qū)動

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

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

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

    Node.js的路由、EJS模板引擎、GET和POST請求講解

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

    Node Sass依賴問題排查思路解析

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

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

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

    nodejs讀寫json文件的簡單方法(必看)

    下面小編就為大家?guī)硪黄猲odejs讀寫json文件的簡單方法(必看)。
    2017-03-03
  • 利用adb shell和node.js實現(xiàn)抖音搶紅包功能(推薦)

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

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

最新評論

克拉玛依市| 张家界市| 修文县| 措勤县| 中卫市| 永德县| 元谋县| 微山县| 固安县| 荣昌县| 绍兴市| 成安县| 万安县| 禄丰县| 额敏县| 焦作市| 光山县| 宁强县| 肥乡县| 苗栗市| 武汉市| 九龙坡区| 伊吾县| 集安市| 化德县| 双鸭山市| 邵阳县| 龙南县| 营山县| 东阿县| 阿荣旗| 林芝县| 河南省| 屯留县| 天峻县| 麻江县| 叙永县| 佳木斯市| 宜川县| 响水县| 梓潼县|