當前位置:編程學習大全網 - 編程語言 - 關於generator異步編程的理解以及如何動手寫

關於generator異步編程的理解以及如何動手寫

關於generator異步編程的理解以及如何動手寫壹個co模塊

generator出現之前,想要實現對異步隊列中任務的流程控制,大概有這麽壹下幾種方式:

回調函數

事件監聽

發布/訂閱

promise對象

第壹種方式想必大家是最常見的,其代碼組織方式如下:

請點擊輸入圖片描述

function fn(url, callback){ var httpRequest;    //創建XHR

httpRequest = window.XMLHttpRequest ? new XMLHttpRequest() :  

window.ActiveXObject ? new ActiveXObject("Microsoft.XMLHTTP") : undefined;

httpRequest.onreadystatechange = function(){ ?if(httpRequest.readystate === 4 && httpRequest.status === 200){  //狀態判斷 ? callback.call(httpRequest.responseXML);

}

};

httpRequest.open("GET", url);

httpRequest.send();

}

fn("text.xml", function(){    //調用函數

console.log(this); //此語句後輸出});

console.log("this will run before the above callback.");  //此語句先輸出

請點擊輸入圖片描述

對於壹個普通的ajax異步請求來說,我麽在請求開始的時候就要告訴他請求成功之後所要執行的動作,因此就可以類似以這種方式組織代碼,控制異步流程。這種調用方式最大的問題就是回調黑洞的問題,壹層回調也還好,但涉及到二層、三層、n層的時候就讓代碼變得復雜很難維護。

第二種方式自己在前段時間使用backbone.js作為技術棧的項目的開發中深有體會,對於每壹個ajax請求都對其分配壹個自定義事件,在ajax成功返回數據的時候,就會觸發自定義的事件完成接下來的動作,控制異步流程,代碼如下:

請點擊輸入圖片描述

請點擊輸入圖片描述

第三種方式和第二種的方式性質上有些類似,如果從發布訂閱的角度來看,on方法相當於訂閱者/觀察者,trigger方法相當於發布者。原理上來說無非就是維護壹個“消息中心”的數組,通過on方法訂閱的事件都會推入“消息中心”數組,最後發布的時候將會匹配“消息中心”數組的事件,進而執行相應的流程。

我們通過jquery的sub/pub插件完成壹個很簡單的演示。

首先,f2向"信號中心"jQuery訂閱"done"信號。

請點擊輸入圖片描述

 jQuery.subscribe("done", f2);

function f1(){

setTimeout(function () {

// f1的任務代碼

jQuery.publish("done");

}, 1000);

}

f1();

請點擊輸入圖片描述

jQuery.publish("done")的意思是,f1執行完成後,向"信號中心"jQuery發布"done"信號,從而引發f2的執行。

第四種方式promise範式,先看壹段代碼:

請點擊輸入圖片描述

我們只要並且僅需要new壹個promise對象,就會發現promise對象的參數函數已經執行了,隔兩秒之後輸出"執行完成"。

接下來再看壹段其實際應用的場景代碼:

請點擊輸入圖片描述

從本質上來看,Promise是壹個構造函數,其本身有all、reject、resolve等方法,同時其原型上有then、catch等方法。通過其用Promise new出來的對象自然就有then、catch方法。然後可以通過then方法中的回調函數,獲取到上壹段異步操作中返回(通過resolve)的數據。從而實現對異步操作的流程控制。

但我的每個函數都得被promise對象包裝壹下,同時壹大堆的then...真是壹個聽蛋疼的事兒...

綜上所述對於異步流程的控制,都有其自身的缺陷,我們最理想的方式便是像操作同步流程那樣實現對異步流程的控制,試想壹下這樣的異步操作流程(加了層層包裝,proxy便是發送壹個異步請求,接下來的代碼便是獲取到異步操作返回的數據,細節可暫時忽略):

請點擊輸入圖片描述

這感覺就是真他媽的舒服,怎麽實現這麽壹個讓人很爽的東西呢,於是我們的主角---偉大的Generator函數登場了。

先理解這麽自己悟的壹句話:

"javascript是單線程的,順序執行壹段代碼,執行到了異步操作,按正常的邏輯走的話就是主隊列中的代碼繼續執行,這時異步隊列中的代碼還未執行,我們繼續執行的代碼也就會發生報錯。那麽解決問題的關鍵就是,我們能夠手動控制代碼的向下執行,配合壹個東西監聽到異步操作的已經正常返回了之後,去手動的操作代碼的執行流程,這樣的話就實現了已同步的方式控制異步代碼的執行"?

那麽問題變成了解決兩個問題。

1、我們是如何實現對於異步操作是否成功返回的監聽。

2、如何手動操作代碼的向下執行。

對於第壹個問題,我們采用的方案是使用promise對象的方式,Promise 的編程思想便是,用於“當xx數據準備完畢,then執行xx動作”這樣的場景,用在這裏再適合不過。

對於第二個問題,我們便是采用偉大的generator生成器函數,其中的yield特性,可以使我們手動的控制代碼的向下執行。

接下來我們實際的解決壹個問題:實現對於讀取文件異步操作的控制,當讀取完文件之後打印讀取的內容。

我們依賴於node環境,首先通過promise對其進行封裝,實現數據成功的監聽。我們手下代碼如下:

請點擊輸入圖片描述

var fs = require('fs');var readFile = function(fileName) { return new Promise(function(resolve,reject) {

fs.readFile(fileName, function(err, data) { if (err) return reject(err);

resolve(data);

})

})

}

請點擊輸入圖片描述

有了這個東西,我們便可以通過其then()表達式,"當數據加載完後,執行某個動作"。那我們執行的動作是啥,自然就是執行下壹步的代碼的操作。繼續看代碼:

請點擊輸入圖片描述

var gen = function* () { var f1 = yield readFile('/Users/dongzhiqiang/Desktop/demo.txt'); var f2 = yield readFile('/Users/dongzhiqiang/Desktop/demo.txt');

console.log('<<<<<<<<<<<<<<<<<<<<<<<',f1.toString());

console.log('>>>>>>>>>>>>>>>>>>>>>>>>',f2.toString());

}

請點擊輸入圖片描述

這個就是壹個generator函數的表達式,在這個函數裏面,遇到generator就會執行類似於return的操作。我們通過next()便可以實現手動的控制代碼的向下執行。

那麽我們如何控制代碼的執行流程呢,看下面壹段:

請點擊輸入圖片描述

var g = gen();

g.next().value.then(function(data){

g.next(data).value.then(function(data){

g.next(data);

});

});

請點擊輸入圖片描述

這段的具體解釋就是,我們通過promise封裝的對象實現了對於異步操作數據返回的監聽,當數據返回的時候,我們就通過next()執行下壹步的操作,同時把上步操作的值帶入到下壹個階段的執行流程之中。

但是上面這段操作很是蛋疼啊,我們要的是壹個能通用的操作流程函數。那麽我們繼續對這段循環操作進行封裝:

請點擊輸入圖片描述

function run(gen){ ?var g = gen(); ?function next(data){ var result = g.next(data); if (result.done) return result.value;

result.value.then(function(data){

?next(data);

});

}

next();

}

run(gen);

請點擊輸入圖片描述

於是壹個非常簡單的co模塊便誕生了。

最終代碼如下:

請點擊輸入圖片描述

我們把函數放到run的執行器裏面,便實現了同步操作異步代碼的過程

  • 上一篇:初中畢業可以學什麽職業?
  • 下一篇:C語言問題求怎麽做?
  • copyright 2024編程學習大全網