Node.js與Golang使用感覺與小結【三】--JS異步流程控制(序列模式、併發模式、有限...

Node.js與Golang使用感覺與小結 php

目錄 前端

1、互聯網的基石TCP/IP協議 node

2、HTTP服務器編寫與編程語言無關 程序員

3、構建HTTP服務器須要掌握的知識點 數據庫

4、HTTP協議基礎 編程

5、Node.js簡介 後端

6、是前端選擇,仍是後端的福音? 瀏覽器

7、Node.js與傳統php-fpm模式之間的對比 服務器

8、安裝Node.js 網絡

9、讓瀏覽器讀懂你的心--Content-Type

10、向瀏覽器發送文件

11、Node.js異步流程控制(序列模式、併發模式、有限併發模式)

12、靜態資源文件的簡單優化

十3、如今還需少些什麼?

十4、Golang 簡介

十5、爲何要用Golang 

十6、安裝Golang開發環境

十7、如何使用Golang 編寫一個HTTP服務器?

十8、Golang http包與Node.jshttp模塊對比


11、Node.js異步流程控制(序列模式、併發模式、有限併發模式)

   Javascript在基本語法上與其它大部份C派生的語言沒有太多區別,你可能很容易從其它語言過分到Javascript。不少從其它語言轉到Javascript來的用戶,在用一段時間以後極可能對這門語言又愛又恨,特別是對於異步流程的控制。

  對於大部份異步編程的模型來講,大可能是事件驅動型且是基於進程來編碼。這樣爲咱們帶來了諸多好處,咱們沒必要去處理爲了實現一樣目的而作的多線程模型裏面的問題。多線程編程裏面,因爲多個線程訪問的內存塊是同樣的,因此可能會出現死鎖、共享競爭等問題。

   異步編程是什麼暱?打個比方,你須要準備一個豐富的晚餐,其中有炒菜與熬湯。若是是阻塞式同步編程的話,你須要先去炒菜,等菜炒好了,而後在去熬湯。在炒菜的過程當中你不能在作其它事情,只有兩樣都好了的時候你才能開始你的晚餐。那異步會怎麼作暱?能夠一邊熬湯,一邊炒菜。菜炒好的時候,可能湯也好了。而後你就能夠開動了。若是以每個步驟的時間來計算的話,二者的時間成本里面顯然異步要能作的事情更多。

     阻塞式同步編程:炒菜(5分鐘)+熬湯(30分鐘)=開飯時用了35分鐘

     異步式編程模型:【炒菜(5分鐘)熬湯(30分鐘)】=開飯時用了30分鐘

     經過以上的例子能夠知道異步編程模型在徹底任務的時間比以及時間利用上要高效的多。

研究異步流處理的意義



   異步流程控制很差,很容易寫出巢式風格代碼,隨着項目的深刻與功能的擴展會發現愈來愈難以維護,或許這也是不少用不習慣JS的程序員不喜歡 JS的一個緣由 之一吧。

async1(function(input, result1) {

 async2(function(result2) {

    async3(function(result3) {

      async4(function(result4) {

       async5(function(output) { // do something with output });

        });

      });

    });

   })

以上這種代碼即是咱們能夠在某些程序員同窗中能夠看到過的代碼,特別是操做數據庫部份常常容易出現這樣的代碼。之前一個同事說用過Node.js開發過一次程序後就不想再次開發程序,由於已經受夠了這種難以維護的代碼。當時我向他推薦了一些相似如AsyncjsStep之類的流程控制的庫,最後那個項目是否採用就不得而知。

巢式風格的代碼 可能引發的問題


解決之道

目前社區有三種觀點:

(一)

     將代碼分離再分離。雖然仍是有存在回調函數的問題,可是代碼的複雜度獲得必定的下降。舉例而言(nodejs爲例,但異步流程控制問題並非NODEJS的專利)

     var http=require(‘http);

     http.createServer(function(request,response){

     response.writeHead(200,{‘Content-Type:text/plain});

     response.write(‘hello);

     response.end();

     }).listen(80,’127.0.0.1);

    上面代碼看似沒有什麼問題,可是若是這個時候我要向瀏覽器端返回一個文件,若是文件沒存在返回404。又若是我要增長數據庫訪問功能。若是代碼全放到這個裏面。那維護起來會是怎麼樣的狀況!


      var http=require(‘http);

      function requesthander(req,res){

      res.writeHead(200,{‘Content,text/plain});

      res.wirte(‘hello);

     res.end();

   }

     http.createServer(requesthander).listen(80,’127.0.0.1);

     這種分離方法被早期的一些設計所使用,其主體思想是將巢狀代碼給展平,分離每一步邏輯。可是這個也帶來了另外的問題。雖然實現了分享,可是代碼之間過於鬆散,代碼之間的關係必須層層深刻的去理解。

  (二)

     將代碼進一步抽象,使用統一的庫或工具包去組織代碼,不破壞原有代碼的邏輯與組織。就Javascript 世界來講出現了不少優秀的庫。如Setp,Async(如網易開源的遊戲開發框架)之類的庫,他們提供了豐富的流控制接口,利用這些接口方法你能夠很好的組織或利用好你的代碼,減輕開發的負擔。

使用方式以下:

async.series(
    [
        function(callback) {

            callback(null, ‘Node.js);//第一個函數的執行結果傳給下個
        },
        function(callback) {
            callback(null, ‘JavaScript);//第二個函數的執行結果在傳給最後的回調函數
        },
    ],
    function(err, response) {
        // response is [‘Node.js, JavaScript最後的結果收集。
    }
);

 

三)

  編譯執行。是的,其大概思路是將已有的代碼再經過某種方式編譯一式,最終實現異步的功能。好比國內老趙的Jscex.雖然本人不太喜歡這種風格的方試,可是仍是向你們簡單介紹一下其代碼風格。Eval的使用以及代碼組織方式讓我放棄了使用此庫。

下面開始這次的正題,介紹社區中經常使用的三種js異步流程控制模式

    不管網絡上的代碼怎麼變化,提供了怎麼樣的接口,但目前來講仍舊脫離不開如下三種模式。但願經過簡單的介紹能讓您也寫出一個本身的異步流程控制庫或能瞭解其基本原理也不錯。

    Series(串行模式)

                   -------每次只執行一個任務。

    Fully parallel(全併發模式)

                   -------一次性所有執行

    Limitedly parallel(限制併發模式)

                   -------每次按限制的個數執行併發任務。



異步處理庫須要具有的功能

  一、可以提供控制函數集執行的方法。

  二、可以收集執行過程當中所產生的數據。

  三、提供特殊狀況下的併發限制,由於在某些狀況下對於併發數量是有控制的,不然可能會讓你的系統吃不消。

  四、可以提供方法能在全部函數集確認執行成功後執行。


Series模式

   適用場景:對執行順序有具體要求的情景。

   如查詢多條數據庫且數據庫語句有依賴狀況下  

   又好比說寫文件(目錄權限,文件是否存在,文件權限);

   特色:

   按順序執行函數集

   在運行時每次只運行一個任務

   確保每次運行都在上一個任務完成後執行。

   每一步的數據是可控,可在下一個任務執行時被調用到(數據順序性)。



Fully parallel模式

   適用場景:併發運行多個任務,當全部任務運行完通知回調方法。如多個I/O 

   做而且每一個時長不肯定的時候,可能須要這種模式。

   特色:

   併發運行多個任務,不用等待上一個任務執行完才執行。

   運算執行只能在回調函數中獲得,由於併發模式不能確保執行的順序。


Limited parallel模式

適用場景:系統資源有限,且每一個執行任務消耗資源較多。

 

特色:

 

併發運行多個限定任務,不用等待上一個任務執行完才執行。

 

運算執行結果只能在回調函數中獲得,由於併發模式不能確保執行的順序。

 

 

 

附三種模式的代碼:、

/**
*  Series flow
*@param {Array} callbacks
*@param {Function} last
*@example series([fun1(next){
someFun();
next();//or next(value)
},fun2(next){
   someFun();
next();//or next(value)
}])
**/
function series(callbacks, last) {
  var results = [];
  function next() {
    var callback = callbacks.shift();
    if(callback) {
      callback(function() {
        results.push(Array.prototype.slice.call(arguments));
        next();
      });
    } else {
      last(results);
    }
  }
  next();
}
// Example task
function async(arg, callback) {
  var delay = Math.floor(Math.random() * 5 + 1) * 100; // random ms
  console.log('async with \''+arg+'\', return in '+delay+' ms');
  setTimeout(function() { callback(arg * 2); }, delay);
}
function final(results) { console.log('Done', results); }


series([
  function(next) { async(1, next); },
  function(next) { async(2, next); },
  function(next) { async(3, next); },
  function(next) { async(4, next); },
  function(next) { async(5, next); },
  function(next) { async(6, next); }
], final);
--------------------------------------------------------

function fullParallel(callbacks, last) {
  var results = [];
  var result_count = 0;
  callbacks.forEach(function(callback, index) {
    callback( function() {
      results[index] = Array.prototype.slice.call(arguments);
      result_count++;
      if(result_count == callbacks.length) {
        last(results);
      }
    });
  });
}
// Example task
function async(arg, callback) {
  var delay = Math.floor(Math.random() * 5 + 1) * 100; // random ms
 // console.log('async with \''+arg+'\', return in '+delay+' ms');
  for(var i =0;i<100000000;i++){


  }
  setTimeout(function() { callback(arg * 2); }, delay);
  console.log(+new Date());
 
}
function final(results) { console.log('Done', results); }


fullParallel([
  function(next) { async(1, next); },
  function(next) { async(2, next); },
  function(next) { async(3, next); },
  function(next) { async(4, next); },
  function(next) { async(5, next); },
  function(next) { async(6, next); }
], final);

-------------------------------------------------

function limited(limit, callbacks, last) {
  var results = [];
  var running = 1;
  var task = 0;
  function next(){
    running--;
    if(task == callbacks.length && running == 0) {
      last(results);
    }
    while(running < limit && callbacks[task]) {
      var callback = callbacks[task];
      (function(index) {
        callback(function() {
          results[index] = Array.prototype.slice.call(arguments);
          next();
        });
      })(task);
      task++;
      running++;
    }
  }
  next();
}
// Example task
function async(arg, callback) {
  var delay = Math.floor(Math.random() * 5 + 1) * 1000; // random ms
  console.log('async with \''+arg+'\', return in '+delay+' ms');
  setTimeout(function() {
    var result = arg * 2;
    console.log('Return with \''+arg+'\', result '+result);
    callback(result);
  }, delay);
}
function final(results) { console.log('Done', results); }


limited(2, [
  function(next) { async(1, next); },
  function(next) { async(2, next); },
  function(next) { async(3, next); },
  function(next) { async(4, next); },
  function(next) { async(5, next); },
  function(next) { async(6, next); }
], final);

---------------------------------------------

參考文獻 :http://book.mixu.net/ 

 

==========================================================

關於做者:

網名:念念之間    現居:深圳

Email:jinfei121@qq.com

QQ:490821193

  從03年開始玩論壇,但正式寫代碼是前幾年的事情。剛工做前兩主要寫php程序,目前主要專職寫

Javascript.平時喜歡研究各類新鮮技術~~ 

  Node與Golang的粉絲~~

==========================================================

相關文章
相關標籤/搜索