Node.js中的異步


title: Node.js中的異步
categories: tech
tags:

  • 異步
  • Node.js

前言

寫Node.js也有一段時(shí)間了,但一直沒有理解Node.Js中的異步阻桅,同時(shí)我也有了更多的疑問異步回調(diào)和同步回調(diào)的區(qū)別,Node.js采用異步的優(yōu)勢(shì)是什么?如何才能優(yōu)雅的寫Node.js的代碼?

Node.js的單線程

先來看下Node.js的結(jié)構(gòu),分為三層莱褒,如下:

struct.jpg
  • Node.js 標(biāo)準(zhǔn)庫(kù),這部分是由 Javascript 編寫的涎劈,即我們使用過程中直接能調(diào)用的 API广凸。在源碼中的 lib 目錄下可以看到。

  • Node bindings蛛枚,這一層是 Javascript 與底層 C/C++ 能夠溝通的關(guān)鍵谅海,前者通過 bindings 調(diào)用后者,相互交換數(shù)據(jù)蹦浦。實(shí)現(xiàn)在 node.cc

  • 這一層是支撐 Node.js 運(yùn)行的關(guān)鍵扭吁,由 C/C++ 實(shí)現(xiàn)。

    • V8:Google 推出的 Javascript VM,也是 Node.js 為什么使用的是 Javascript 的關(guān)鍵侥袜,它為 Javascript 提供了在非瀏覽器端運(yùn)行的環(huán)境蝌诡,它的高效是 Node.js 之所以高效的原因之一。
    • Libuv:它為 Node.js 提供了跨平臺(tái)枫吧,線程池送漠,事件池,異步 I/O 等能力由蘑,是 Node.js 如此強(qiáng)大的關(guān)鍵闽寡。
    • C-ares:提供了異步處理 DNS 相關(guān)的能力。
    • http_parser尼酿、OpenSSL爷狈、zlib 等:提供包括 http 解析、SSL裳擎、數(shù)據(jù)壓縮等其他的能力涎永。

Node.js 雖然說是用的 Javascript,但只是在開發(fā)時(shí)使用 Javascript 的語(yǔ)法來編寫程序鹿响。真正的執(zhí)行過程還是由 V8 將 Javascript 解釋羡微,然后由 C/C++ 來執(zhí)行真正的系統(tǒng)調(diào)用。

真正執(zhí)行系統(tǒng)調(diào)用的其實(shí)是 Libuv惶我。之前我們提到妈倔,Libuv 本身就是異步和事件驅(qū)動(dòng)的,所以绸贡,當(dāng)我們將 I/O 操作的請(qǐng)求傳達(dá)給 Libuv 之后盯蝴,Libuv 開啟線程來執(zhí)行這次 I/O 調(diào)用,并在執(zhí)行完成后听怕,傳回給 Javascript 進(jìn)行后續(xù)處理捧挺。

nodejs_thread.png

這里面的 I/O 包括文件 I/O 和 網(wǎng)絡(luò) I/O。兩者的底層執(zhí)行略有不同尿瞭。從上面的 Libuv 官網(wǎng)的圖中闽烙,我們可以看到,文件 I/O声搁,DNS 等操作黑竞,都是依托線程池(Thread Pool)來實(shí)現(xiàn)的。而網(wǎng)絡(luò) I/O 這一大類酥艳,包括:TCP摊溶、UDP、TTY 等充石,是由 epoll莫换、IOCP、kqueue 來具體實(shí)現(xiàn)的。

它的單線程指的是自身 Javascript 運(yùn)行環(huán)境的單線程拉岁,Node.js 并沒有給 Javascript 執(zhí)行時(shí)創(chuàng)建新線程的能力坷剧,最終的實(shí)際操作,還是通過 Libuv 以及它的事件循環(huán)來執(zhí)行的喊暖。

異步和同步

異步是對(duì)進(jìn)程在和I/O(有文件I/O惫企、網(wǎng)絡(luò)I/O)交互而言的。在做網(wǎng)絡(luò)請(qǐng)求時(shí)候陵叽,假設(shè)這個(gè)操作需要2s狞尔,同步會(huì)等待這個(gè)2s的請(qǐng)求完成再去做下一個(gè)動(dòng)作;然后異步會(huì)直接進(jìn)行下一個(gè)動(dòng)作巩掺。

下面這段代碼偏序,輸出的是null,因?yàn)榫W(wǎng)絡(luò)I/O這個(gè)請(qǐng)求是異步的胖替,執(zhí)行到異步相關(guān)操作的時(shí)候會(huì)立即返回研儒。

var statusCode = null;
request('https://www.baidu.com/', function(error, response, body) {
        var statusCode =  response.statusCode;
});
console.log(statusCode);

為什么Node.js要設(shè)計(jì)為異步

目前為止我接觸的編程語(yǔ)言都是同步,剛開始寫 Node.js 那是相當(dāng)?shù)牟涣?xí)慣独令,即使現(xiàn)在習(xí)慣了還是覺得這樣編程很麻煩端朵。既然如此那為何要把 Node.js 設(shè)計(jì)成異步的呢?

異步并不會(huì)少程序的運(yùn)行時(shí)間燃箭,不會(huì)因?yàn)槟阌昧水惒匠迥兀樵償?shù)據(jù)庫(kù)的時(shí)間就會(huì)從10ms減少到5ms,無論是同步還是異步遍膜,執(zhí)行的時(shí)間依然是10ms碗硬,異步的作用僅僅是提升了應(yīng)用程序的吞吐量瓤湘。進(jìn)一步說瓢颅,異步 IO 會(huì)減少單個(gè)請(qǐng)求的時(shí)間,去掉單個(gè)請(qǐng)求中那些無意義的等待時(shí)間(從用戶角度來看)弛说。所以單位時(shí)間內(nèi)處理的請(qǐng)求沒有變化(從服務(wù)器監(jiān)督看)挽懦,但每個(gè)請(qǐng)求的處理時(shí)間卻減少了。從這個(gè)角度木人,服務(wù)器也節(jié)約了一些資源——即維持每個(gè)請(qǐng)求的連接消耗的內(nèi)存信柿。

異步這么麻煩應(yīng)該怎么寫

最簡(jiǎn)單的實(shí)現(xiàn)異步的方法就是回調(diào),但是對(duì)于有時(shí)序性的醒第,異步多了之后就成了回調(diào)陷阱渔嚷,代碼會(huì)像下面這個(gè)樣子,瞬間感覺自己智商跟不上了要:

request('https://www.baidu.com/', function(error, response, body) {
    var statusCode =  response.statusCode;
    request('https://www.baidu.com/', function(error, response, body) {
        var statusCode =  response.statusCode;
        request('https://www.baidu.com/', function(error, response, body) {
            var statusCode =  response.statusCode;
        });
    });
});

當(dāng)然現(xiàn)在有很多解決方案稠曼,如下圖所示:

async.png

Node.js如何實(shí)現(xiàn)的異步

nodejs是單線程(single thread)運(yùn)行的形病,通過一個(gè)事件循環(huán)(event-loop)來循環(huán)取出消息隊(duì)列(event-queue)中的消息進(jìn)行處理,處理過程基本上就是去調(diào)用該消息對(duì)應(yīng)的回調(diào)函數(shù)。消息隊(duì)列就是當(dāng)一個(gè)事件狀態(tài)發(fā)生變化時(shí),就將一個(gè)消息壓入隊(duì)列中漠吻。

實(shí)際上量瓜,主線程只會(huì)做一件事情,就是從消息隊(duì)列里面取消息途乃、執(zhí)行消息绍傲,再取消息、再執(zhí)行耍共。當(dāng)消息隊(duì)列為空時(shí)烫饼,就會(huì)等待直到消息隊(duì)列變成非空。而且主線程只有在將當(dāng)前的消息執(zhí)行完成后试读,才會(huì)去取下一個(gè)消息枫弟。這種機(jī)制就叫做事件循環(huán)機(jī)制,取一個(gè)消息并執(zhí)行的過程叫做一次循環(huán)鹏往。

用ajax請(qǐng)求做一個(gè)具體說明:

ajax.png

--EOF--

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末淡诗,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子伊履,更是在濱河造成了極大的恐慌韩容,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,640評(píng)論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件唐瀑,死亡現(xiàn)場(chǎng)離奇詭異群凶,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)哄辣,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,254評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門请梢,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人力穗,你說我怎么就攤上這事毅弧。” “怎么了当窗?”我有些...
    開封第一講書人閱讀 165,011評(píng)論 0 355
  • 文/不壞的土叔 我叫張陵够坐,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我崖面,道長(zhǎng)元咙,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,755評(píng)論 1 294
  • 正文 為了忘掉前任巫员,我火速辦了婚禮庶香,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘简识。我一直安慰自己赶掖,他們只是感情好救军,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,774評(píng)論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著倘零,像睡著了一般唱遭。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上呈驶,一...
    開封第一講書人閱讀 51,610評(píng)論 1 305
  • 那天拷泽,我揣著相機(jī)與錄音,去河邊找鬼袖瞻。 笑死司致,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的聋迎。 我是一名探鬼主播脂矫,決...
    沈念sama閱讀 40,352評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼霉晕!你這毒婦竟也來了庭再?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,257評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤牺堰,失蹤者是張志新(化名)和其女友劉穎拄轻,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體伟葫,經(jīng)...
    沈念sama閱讀 45,717評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡恨搓,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,894評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了筏养。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片斧抱。...
    茶點(diǎn)故事閱讀 40,021評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖渐溶,靈堂內(nèi)的尸體忽然破棺而出辉浦,到底是詐尸還是另有隱情,我是刑警寧澤掌猛,帶...
    沈念sama閱讀 35,735評(píng)論 5 346
  • 正文 年R本政府宣布盏浙,位于F島的核電站,受9級(jí)特大地震影響荔茬,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜竹海,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,354評(píng)論 3 330
  • 文/蒙蒙 一慕蔚、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧斋配,春花似錦孔飒、人聲如沸灌闺。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,936評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)桂对。三九已至,卻和暖如春鸠匀,著一層夾襖步出監(jiān)牢的瞬間蕉斜,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,054評(píng)論 1 270
  • 我被黑心中介騙來泰國(guó)打工缀棍, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留宅此,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,224評(píng)論 3 371
  • 正文 我出身青樓爬范,卻偏偏與公主長(zhǎng)得像父腕,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子青瀑,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,974評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容