国产日韩欧美一区二区三区三州_亚洲少妇熟女av_久久久久亚洲av国产精品_波多野结衣网站一区二区_亚洲欧美色片在线91_国产亚洲精品精品国产优播av_日本一区二区三区波多野结衣 _久久国产av不卡

?

Promise方式實現(xiàn)Node.js應(yīng)用的實踐①

2017-05-17 10:00鄧森泉楊海波中國科學(xué)院沈陽計算技術(shù)研究所沈陽0168中國科學(xué)院大學(xué)北京100049
計算機系統(tǒng)應(yīng)用 2017年4期
關(guān)鍵詞:開發(fā)人員數(shù)據(jù)結(jié)構(gòu)代碼

鄧森泉, 楊海波(中國科學(xué)院 沈陽計算技術(shù)研究所, 沈陽 0168)(中國科學(xué)院大學(xué), 北京 100049)

Promise方式實現(xiàn)Node.js應(yīng)用的實踐①

鄧森泉1,2, 楊海波11(中國科學(xué)院 沈陽計算技術(shù)研究所, 沈陽 110168)2(中國科學(xué)院大學(xué), 北京 100049)

Node.js是目前非?;馃岬募夹g(shù)之一, 它是運行在服務(wù)器端的JavaScript執(zhí)行環(huán)境. Node.js借助JavaScript的事件驅(qū)動機制加上V8高性能引擎, 使得編寫高性能Web服務(wù)輕而易舉. Node.js在處理異步問題時一般采用的是callback回調(diào)的方式, 但callback回調(diào)的方式存在Callback Hell的問題, 無論是閱讀還是調(diào)試都很不方便, 甚至無法獲取代碼的堆棧. 基于Node.js平臺, 采用Promise方式, 編寫了一套網(wǎng)絡(luò)爬蟲的應(yīng)用, 在編寫過程中詳細的描述了如何使用Promise方式處理異步回調(diào)問題.

Node.js; Promise; Web應(yīng)用

Node.js是建立在Chrome V8引擎的javaScript運行時之上的平臺, 用于構(gòu)建快速、可擴展的Web應(yīng)用程序. Node.js采用單線程、事件驅(qū)動、非阻塞的I/O模型, 這些特性不僅帶來了巨大的性能提升, 還減少了多線程程序設(shè)計的復(fù)雜性, 進而提高了開發(fā)效率,使其輕量又高效. 傳統(tǒng)的Node.js在處理異步問題時,一般采用的是callback回調(diào)的方式. callback回調(diào)存在一個很嚴重的金字塔問題——大量的回調(diào)函數(shù)慢慢向右側(cè)屏幕延伸的一種狀態(tài).

Promise是異步編程的一種解決方案, 比傳統(tǒng)的解決方案——回調(diào)函數(shù)和事件, 更合理和強大. 它最早由javascript社區(qū)提出和實現(xiàn), 目前最新的JavaScript語言標準ES6已將其寫進了標準中, 統(tǒng)一了用法, 原生提供了Promise對象. 借助Promise對象, 可以將異步操作以同步操作的流程表達出來, 避免了層層嵌套的回調(diào)函數(shù).

本文就是采用Promise方式在Node.js平臺上搭建了一個網(wǎng)絡(luò)爬蟲的應(yīng)用. 本文首先介紹了Node.js平臺以及其相關(guān)的一些特點和概念, 然后在此基礎(chǔ)上, 針對其傳統(tǒng)的callback的回調(diào)方式的“回調(diào)地獄”等問題,引入了Promise對象來處理這種異步回調(diào)的問題. 通過深入分析Promise對象的理論知識以及規(guī)范, 將其合理地運用到網(wǎng)絡(luò)爬蟲的應(yīng)用中去. 最后通過爬取一個課程網(wǎng)站的視頻課程信息, 充分展示了Node.js平臺的強大和方便, 以及Promise對象在處理異步回調(diào)問題上的優(yōu)越性以及新思路.

1 Node.js平臺介紹

Node.js是一位叫Ryan Dahl的程序員發(fā)明的. 他的工作是用C/C++寫高性能Web服務(wù). 對于高性能,異步IO、事件驅(qū)動是基本原則, 但是用C/C++寫就太痛苦了. 于是Ryan開始設(shè)想用高級語言開發(fā)Web服務(wù).他評估了很多種高級語言, 發(fā)現(xiàn)很多語言雖然同時提供了同步IO和異步IO, 但是開發(fā)人員一旦用了同步IO, 他們就再也懶得寫異步IO了, 所以, 最終, Ryan瞄向了JavaScript. 因為JavaScript是單線程執(zhí)行, 根本不能進行同步IO操作, 所以, JavaScript的這一“缺陷”導(dǎo)致了它只能使用異步IO.

選定了開發(fā)語言, 還要有運行時引擎. Ryan曾考慮過自己寫一個, 不過明智地放棄了, 因為V8就是開源的JavaScript引擎. 讓Google投資去優(yōu)化V8, 我們只管拿過來用就好了.

于是在2009年, Ryan正式推出了基于JavaScript語言和V8引擎的開源Web服務(wù)器項目, 命名為Node.js. Node第一次把JavaScript帶入到后端服務(wù)器開發(fā), 加上世界上已經(jīng)有無數(shù)的JavaScript開發(fā)人員,所以Node.js一下子就火了起來.

Node.js架構(gòu)如圖1所示.

圖1 Node.js架構(gòu)

Node.js主要特點是(1)時間驅(qū)動、異步編程; (2)單進程單線程.

1.1 事件驅(qū)動、異步編程

事件驅(qū)動并不是Node.js專屬, 在某些傳統(tǒng)語言的網(wǎng)絡(luò)編程中, 我們會用到回調(diào)函數(shù), 比如當socket資源達到某種狀態(tài)時, 注冊的回調(diào)函數(shù)就會執(zhí)行. Node.js的設(shè)計思想中以事件驅(qū)動為核心, 它提供的絕大多數(shù)API都是基于事件的、異步的風(fēng)格. 以Net模塊為例, 其中的net.Socket對象就有以下事件: connect、data、end、timeout、drain、error、close等, 使用Node.js的開發(fā)人員需要根據(jù)自己的業(yè)務(wù)邏輯注冊相應(yīng)的回調(diào)函數(shù). 這些回調(diào)函數(shù)都是異步執(zhí)行的, 這意味著雖然在代碼結(jié)構(gòu)中, 這些函數(shù)看似是依次注冊的, 但是它們并不依賴于自身出現(xiàn)的順序, 而是等待相應(yīng)的事件觸發(fā). 事件驅(qū)動、異步編程的設(shè)計重要的優(yōu)勢在于, 充分利用了系統(tǒng)資源, 執(zhí)行代碼無須阻塞等待某種操作完成, 有限的資源可以用于其他的任務(wù).此類設(shè)計非常適合于后端的網(wǎng)絡(luò)服務(wù)編程, Node.js的目標也在于此. 在服務(wù)器開發(fā)中, 并發(fā)的請求處理是個大問題, 阻塞式的函數(shù)會導(dǎo)致資源浪費和時間延遲.通過事件注冊、異步函數(shù), 開發(fā)人員可以提高資源的利用率, 性能也會改善.

從Node.js提供的支持模塊中, 我們可以看到包括文件操作在內(nèi)的許多函數(shù)都是異步執(zhí)行的, 這和傳統(tǒng)語言存在區(qū)別, 而且為了方便服務(wù)器開發(fā), Node.js的網(wǎng)絡(luò)模塊特別多, 包括HTTP、DNS、NET、UDP、HTTPS、TLS等, 開發(fā)人員可以在此基礎(chǔ)上快速構(gòu)建Web服務(wù)器.

比如搭建一個簡單的http服務(wù)器:

1.2 單進程單線程

1.2.1 高性能

Node.js單線程模式避免了傳統(tǒng)php那樣頻繁創(chuàng)建、切換線程的花銷, 執(zhí)行速度更快. 而且, 資源占用小, Node.js在大負荷下對內(nèi)存占用任然很低.

1.2.2 線程安全

單線程的node.js還保證了絕對的線程安全, 不用擔心統(tǒng)一變量同時被多個線程進行讀寫而造成程序崩潰. 線程安全的同時也解放了開發(fā)人員, 免去了多線程編程中忘記對變量加鎖或者解鎖造成的隱患.

2 Promise

Promise主要解決JavaScript中異步的場景. Promise是個對象, 同JavaScript中其它對象沒什么區(qū)別, 但同時它也是一個規(guī)范, 針對異步操作約定了統(tǒng)一的接口, 表示一個一步操作最終的結(jié)果, 以同步的方式來寫代碼, 執(zhí)行的操作是異步的, 但是又保證程序的執(zhí)行順序是同步的. 這原本是JavaScript社區(qū)的一個規(guī)范的構(gòu)想, 現(xiàn)在已經(jīng)被加入到了ES6的語言標準中, Firefox和Chrome等瀏覽器已經(jīng)對它進行了實現(xiàn). 2.1同步與異步

JS引擎是單線程的. 這意味著在任何環(huán)境中, 只有一段JS代碼會被執(zhí)行. 每個函數(shù)是一個不可分割的片段或者代碼塊. 當JS引擎開始執(zhí)行一個函數(shù)(比如回調(diào)函數(shù))時, 它就會把這個函數(shù)執(zhí)行完, 只有執(zhí)行完這段代碼才會繼續(xù)執(zhí)行后面的代碼. 這就是JS中的同步. Promise對象的then()方法就是同步處理每個Promise對象.

異步是指在執(zhí)行一段代碼時, 這段代碼依賴一些其他的操作或者數(shù)據(jù), 這時就不用等待數(shù)據(jù)或者操作的返回, 直接執(zhí)行下一段代碼, 當有數(shù)據(jù)或操作返回時再去響應(yīng)之前的代碼, 從而提高代碼執(zhí)行的效率.

2.2 Promise對象的狀態(tài)

Promise對象只有三種狀態(tài):

(1) Pending: 初始狀態(tài), 進行中.

(2) Resolved(或Fulfilled): 成功的操作.

(3) Rejected: 失敗的操作.

(1) Promise對象的狀態(tài)不受外界影響.

Promise對象代表一個異步操作, 有三種狀態(tài): Pending(進行中)、Resolved(已完成, 又稱Fulfilled)和Rejected(已失敗). 只有異步操作的結(jié)果, 可以決定當前是哪一種狀態(tài), 任何其他操作都無法改變這個狀態(tài).

(2) Promise對象一旦狀態(tài)改變, 就不會再變, 任何時候都可以得到這個結(jié)果.

Promise對象的狀態(tài)改變, 只有兩種可能: 從Pending變?yōu)镽esolved和從Pending變?yōu)镽ejected. 只要這兩種情況發(fā)生, 狀態(tài)就凝固了, 不會再變了, 會一直保持這個結(jié)果. 就算改變已經(jīng)發(fā)生了, 再對Promise對象添加回調(diào)函數(shù), 也會立即得到這個結(jié)果.

2.3 Promise的核心方法

Promise對象的核心部件是它的then方法, 它的作用是為Promise實例添加狀態(tài)改變時的回調(diào)函數(shù). then方法接受兩個回調(diào)函數(shù)作為參數(shù). 第一個回調(diào)函數(shù)是Promise對象的狀態(tài)變?yōu)镽esolved時調(diào)用, 第二個回調(diào)函數(shù)是Promise對象的狀態(tài)變?yōu)镽ejected時調(diào)用. 其中,第二個函數(shù)是可選的, 不一定要提供. 這兩個函數(shù)都接受Promise對象傳出的值作為參數(shù).

Promise對象另一個核心方法是它的catch方法,用于指定發(fā)生錯誤時的回調(diào)函數(shù), 是then(null, rejection)的別名. catch方法可以捕捉promise實例中的異常還能捕獲在它之前太狠方法中發(fā)生的異常, 所以在實際的使用中, 多用catch方法來取代then(null, rejection)處理異常.

3 爬蟲應(yīng)用設(shè)計與實現(xiàn)

3.1 模塊加載

新建一個promise_crawler.js文件, 首先把需要的相應(yīng)的模塊加載進來.

http模塊: 主要用于搭建 HTTP 服務(wù)端和客戶端,使用 HTTP 服務(wù)器或客戶端功能必須調(diào)用 http 模塊;

bluebird模塊: Promise類庫(在最新的Node.js里已經(jīng)引入了Promise模塊, 可直接使用, 但考慮到兼容性問題, 本例中采用bluebird模塊);

cheerio模塊: 類似于前端的jQuery, 能夠簡單方便地操作裝在后臺的html.

代碼如下:

3.2 組織數(shù)據(jù)結(jié)構(gòu)

首先在chrome瀏覽器中打開需要爬取的網(wǎng)頁, 同時打開控制臺查看網(wǎng)頁html DOM結(jié)構(gòu), 分析出所需信息, 組織好數(shù)據(jù)結(jié)構(gòu), 然后根據(jù)DOM結(jié)構(gòu)去獲取所需信息. 如圖2所示.

圖2 網(wǎng)頁及DOM結(jié)構(gòu)

分析所需數(shù)據(jù), 組織好數(shù)據(jù)結(jié)構(gòu):

3.3 Promise主要流程

本例中完成的主要功能是, 同時爬取一個課程網(wǎng)站的多個頁面, 獲取相關(guān)信息, 然后將數(shù)據(jù)按照組織好的數(shù)據(jù)結(jié)構(gòu)打印出來.

核心代碼如下:

代碼中所用到的Promise.all方法用于將多個Promise實例, 包裝成一個新的Promise實例.

該方法接收一個Promise對象數(shù)組作為參數(shù), p1、p2、p3都是Promise對象的實例.

p的狀態(tài)由p1、p2、p3決定, 分成兩種情況.

(1) 只有p1、p2、p3的狀態(tài)都變成Resolved, p的狀態(tài)才會變成Resolved, 此時p1、p2、p3的返回值組成一個數(shù)組, 傳遞給p的回調(diào)函數(shù).

(2) 只要p1、p2、p3之中有一個被rejected, p的狀態(tài)就變成Rejected, 此時第一個被Rejected的實例的返回值, 會傳遞給p的回調(diào)函數(shù).

3.4 相關(guān)函數(shù)實現(xiàn)

3.4.1 爬取頁面getPageAsync(url)

通過http模塊的get方法爬取頁面數(shù)據(jù), 最后返回一個Promise對象, 方便異步處理.

核心代碼如下:

3.4.2 過濾數(shù)據(jù)filterChapters(html)

過濾出每個頁面所需的數(shù)據(jù), 然后按一定的數(shù)據(jù)結(jié)構(gòu)組織起來.

核心代碼如下:

3.4.3 打印數(shù)據(jù)printCourseInfo(coursesData)

將爬取到的數(shù)據(jù), 按照組織好的數(shù)據(jù)結(jié)構(gòu)打印出來.

核心代碼如下:

3.4 實驗結(jié)果

執(zhí)行promise_crawler.js文件, 即可看到輸出的相關(guān)信息如圖3.

圖3 輸出的相關(guān)信息

實驗中同爬取了4個頁面, 可以看到, 實驗結(jié)果是按照代碼中設(shè)定好的數(shù)據(jù)結(jié)構(gòu)爬取并打印出來的,符合實驗預(yù)期. Promise對象是基于異步的方式來處理程序的. 爬取每個頁面時, 不用等待頁面的數(shù)據(jù)處理完畢再去爬取下一個頁面, 而是無阻塞不間斷的去爬取每個頁面, 當有異步的數(shù)據(jù)返回時調(diào)用Promise對象的resolve()方法去處理, 出現(xiàn)錯誤異常時調(diào)用reject()方法去解決. 當有多個Promise對象時, 調(diào)用then(onFulfilled)方法, 同步處理每個Promise對象, 一旦處理哪個Promise對象出錯時, 可以立即調(diào)用catch方法處理異常, 中止程序往下執(zhí)行, 及時發(fā)現(xiàn)錯誤.而且onFulfilled()方法每次返回的是新的Promise對象,這樣保證了then()可以一直鏈式調(diào)用下去, 提高了程序的效率和可靠性.

4 結(jié)語

Node.js作為一門新興的技術(shù), 打通了前后端的界限. 由于采用事件驅(qū)動和無阻塞模型, 可以很方便的構(gòu)建高效、可擴展的網(wǎng)絡(luò)應(yīng)用, 這是Node.js最大的一個優(yōu)點, 同時也是最大的一個缺點, 由于事件驅(qū)動和無阻塞模型是建立在callback這種回調(diào)方式上的, 隨著回調(diào)的增加, 代碼嵌套的層次就會增加, 這樣很容易陷入“回調(diào)地獄”, 這種代碼難以編寫, 難以理解而且難以維護.

Promise對象是解決Node.js中異步回調(diào)的一種很有效的方式. 借助Promise對象, 可以將異步操作以同步操作的流程表達出來, 避免了層層嵌套的回調(diào)函數(shù).在保證異步回調(diào)的基礎(chǔ)上又實現(xiàn)了多個promise對象之間的同步順序, 使程序能快速高效的執(zhí)行下去, 給我們的開發(fā)帶來很大的便利.

1 顧寧,劉家茂,柴曉路.Web Services原理與研發(fā)實踐.北京:機械工業(yè)出版社,2006.

2 樸靈.深入淺出Node.js.北京:人民郵電出版社,2013.

3 趙昆.改變Web開發(fā)格局的新技術(shù)node.js.程序員, 2011,(7):124–125.

4 Burnhamt. Javascript異步編程:設(shè)計快速響應(yīng)的網(wǎng)絡(luò)應(yīng)用.北京:人民郵電出版社.

5 Node.js官方網(wǎng)站.http://www.nodejs.org.

6 Getify. Promise: The Inversion Problem(part 2), https://blog. getify.com/promise-part-2/. [2014-5-19].

7 Brett M. What is node? California: O’Reily Media, 2011.

Web Application Based on Node.js in the Way of Promise

DENG Sen-Quan1,2, YANG Hai-Bo112(Shenyang Institute of Computing Technology, Chinese Academy of Sciences, Shenyang 110168, China) (University of Chinese Academy of Sciences, Beijing 100049, China)

Node.js is one of the most popular technologies at present, and it is the JavaScript execution environment running on the server. With event-driven mechanism Node.js JavaScript plus high-performance V8 engine, it’s easy to achieve high-performance Web services. When Node.js deals with the problem of asynchronous, it generally uses callback method, but there are Callback Hell problems in the way of callback. Whether reading or debugging is very inconvenient, it is even impossible to get the code stack. Based on Node.js platform, using Promise method, we realize a Web crawler application. We describe in detail how to use the Promise approach to deal with the problem of asynchronous callback during the application process.

Node.js; Promise; Web application

2016-07-26;收到修改稿時間:2016-08-25

10.15888/j.cnki.csa.005700

猜你喜歡
開發(fā)人員數(shù)據(jù)結(jié)構(gòu)代碼
數(shù)據(jù)結(jié)構(gòu)線上線下混合教學(xué)模式探討
重典型應(yīng)用,明結(jié)構(gòu)關(guān)系
為什么會有“數(shù)據(jù)結(jié)構(gòu)”?
Semtech發(fā)布LoRa Basics 以加速物聯(lián)網(wǎng)應(yīng)用
創(chuàng)世代碼
創(chuàng)世代碼
創(chuàng)世代碼
創(chuàng)世代碼
后悔了?教你隱藏開發(fā)人員選項
三星SMI擴展Java論壇 開發(fā)人員可用母語