• 
    

    
    

      99热精品在线国产_美女午夜性视频免费_国产精品国产高清国产av_av欧美777_自拍偷自拍亚洲精品老妇_亚洲熟女精品中文字幕_www日本黄色视频网_国产精品野战在线观看 ?

      Node.js異步編程模式探討

      2018-10-18 08:38:28周安輝
      關(guān)鍵詞:控制權(quán)調(diào)用高階

      周安輝

      (內(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ù)計算出返回值時,并不表示函數(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ù)隊列的執(zhí)行,是實(shí)現(xiàn)非阻塞I/O異步編程的關(guān)鍵機(jī)制。當(dāng)Node.js啟動時,將初始化event loop,處理那些可能做異步API調(diào)用、定制計時器或調(diào)用process.nextTick()的輸入腳本,然后開始處理event loop。

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

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

      每個階段都有一個可執(zhí)行的回調(diào)函數(shù)的FIFO隊列。盡管每個階段具有自己特殊方式,通常,當(dāng)事件循環(huán)進(jìn)入一個給定的階段,它將執(zhí)行這個階段的任何特定操作,然后執(zhí)行在這個階段的隊列中的回調(diào)函數(shù),直到隊列為空或者回調(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)格的異步編程會有較大幫助,并且會逐步喜歡上它的簡明與高效。

      猜你喜歡
      控制權(quán)調(diào)用高階
      神農(nóng)科技集團(tuán)正式接收廣譽(yù)遠(yuǎn)控制權(quán)
      有限圖上高階Yamabe型方程的非平凡解
      高階各向異性Cahn-Hilliard-Navier-Stokes系統(tǒng)的弱解
      滾動軸承壽命高階計算與應(yīng)用
      哈爾濱軸承(2020年1期)2020-11-03 09:16:02
      核電項目物項調(diào)用管理的應(yīng)用研究
      LabWindows/CVI下基于ActiveX技術(shù)的Excel調(diào)用
      FF陷控制權(quán)爭奪漩渦
      汽車觀察(2018年10期)2018-11-06 07:05:06
      上市公司控制權(quán)爭奪中獨(dú)立董事的義務(wù)——以萬華之爭為例
      基于系統(tǒng)調(diào)用的惡意軟件檢測技術(shù)研究
      基于Bernstein多項式的配點(diǎn)法解高階常微分方程
      化隆| 明溪县| 香港 | 昌吉市| 介休市| 安仁县| 沙雅县| 榆树市| 彭山县| 仁怀市| 平山县| 图们市| 古蔺县| 斗六市| 清镇市| 边坝县| 革吉县| 柳林县| 潢川县| 岚皋县| 桐庐县| 子长县| 正阳县| 分宜县| 巴林左旗| 鄂尔多斯市| 玉龙| 荥经县| 红安县| 娄底市| 鹤山市| 攀枝花市| 广饶县| 仁布县| 静海县| 建始县| 石狮市| 哈密市| 滕州市| 临沂市| 杭锦旗|