成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

ES6 Generator實(shí)現(xiàn)協(xié)同程序

MudOnTire / 3274人閱讀

摘要:關(guān)鍵字表示代碼在該處將會(huì)被阻塞式暫停阻塞的僅僅是函數(shù)代碼本身,而不是整個(gè)程序,但是這并沒有引起函數(shù)內(nèi)部自頂向下代碼的絲毫改變。通過實(shí)現(xiàn)模式在通過實(shí)現(xiàn)理論的過程中已經(jīng)有一些有趣的探索了。

至此本系列的四篇文章翻譯完結(jié),查看完整系列請(qǐng)移步blogs

由于個(gè)人能力知識(shí)有限,翻譯過程中難免有紕漏和錯(cuò)誤,望不吝指正issue

ES6 Generators: 完整系列

The Basics Of ES6 Generators

Diving Deeper With ES6 Generators

Going Async With ES6 Generators

Getting Concurrent With ES6 Generators

如果你已經(jīng)閱讀并消化了本系列的前三篇文章:第一篇、第二篇、第三篇,那么在此時(shí)你已經(jīng)對(duì)如何使用ES6 generator函數(shù)胸有成竹,并且我也衷心希望你能夠受到前三篇文章的鼓舞,實(shí)際去使用一下generator函數(shù)(挑戰(zhàn)極限),探究其究竟能夠幫助我們完成什么樣的工作。

我們最后一個(gè)探討的主題可能和一些前沿知識(shí)有關(guān),甚至需要?jiǎng)幽X筋才能夠理解(誠實(shí)的說,一開始我也有些迷糊)。花一些時(shí)間來練習(xí)和思考這些概念和示例。并且去實(shí)實(shí)在在的閱讀一些別人寫的關(guān)于此主題的文章。

此刻你花時(shí)間(投資)來弄懂這些概念對(duì)你長遠(yuǎn)來看是有益的。并且我完全深信在將來JS處理復(fù)雜異步的操作能力將從這些觀點(diǎn)中應(yīng)運(yùn)而生。

正式的CSP(Communicating Sequential Processes)

起初,關(guān)于該主題的熱情我完全受啟發(fā)于 David Nolen @swannodette的杰出工作。嚴(yán)格說來,我閱讀了他寫的關(guān)于該主題的所有文章。下面這些鏈接可以幫助你對(duì)CSP有個(gè)初步了解:

"Communicating Sequential Processes"

"ES6 Generators Deliver Go Style Concurrency"

"Extracting Processes"

OK,就我在該主題上面的研究而言,在開始寫JS代碼之前我并沒有編寫Clojure語言的背景,也沒有使用Go和ClojureScript語言的經(jīng)驗(yàn)。在閱讀上面文章的過程中,我很快就發(fā)現(xiàn)我有一點(diǎn)弄不明白了,而不得不去做一些實(shí)驗(yàn)性學(xué)習(xí)或者學(xué)究性的去思考,并從中獲取一些有用的知識(shí)。

在這個(gè)過程中,我感覺我達(dá)到了和作者相同的思維境界,并且追求相同的目標(biāo),但是卻采取了另一種不那么正規(guī)的思維方式。

我所努力并嘗試去構(gòu)建一個(gè)更加簡單的Go語言風(fēng)格的CSP(或者ClojureScript語言中的core.async)APIs,并且(我希望)竟可能的保留那些潛在的能力。在閱讀我文章的那些聰明的讀者一定能夠容易的發(fā)現(xiàn)我對(duì)該主題研究中的一些缺陷和不足,如果這樣的話,我希望我的研究能夠演進(jìn)并持續(xù)發(fā)展下去,我也會(huì)堅(jiān)持和我廣大的讀者分享我在CSP上的更多啟示。

分解 CSP 理論(一點(diǎn)點(diǎn))

CSP究竟是什么呢?在CSP概念下講述的“communicating”、“Sequential”又是什么意思呢?“processes”有代表什么?

首先,CSP的概念是從Tony Hoare的書 "Communicating Sequential Processes"中首次被提及。這本書主要是一些CS理論上的東西,但是如果你對(duì)一些學(xué)術(shù)上的東西很感興趣,相信這本書是一個(gè)很好的開端。在關(guān)于CSP這一主題上我絕不會(huì)從一些頭疼的、難懂的計(jì)算機(jī)科學(xué)知識(shí)開始,我決定從一些非正式入口開始關(guān)于CSP的討論。

因此,讓我們先從“sequential”這一概念入手,關(guān)于這部分你可能已經(jīng)相當(dāng)熟悉,這也是我們?cè)?jīng)討論過的單線程行為的另一種表述或者說我們?cè)谕叫问降腅S6 generator函數(shù)中也曾遇到過。

回憶如下的generator函數(shù)語法:

function *main() {
    var x = yield 1;
    var y = yield x;
    var z = yield (y * 2);
}

上面代碼片段中的語句都按順序一條接一條執(zhí)行執(zhí)行,同一時(shí)間不能夠執(zhí)行多條語句。yield 關(guān)鍵字表示代碼在該處將會(huì)被阻塞式暫停(阻塞的僅僅是 generator 函數(shù)代碼本身,而不是整個(gè)程序),但是這并沒有引起 *main() 函數(shù)內(nèi)部自頂向下代碼的絲毫改變。是不是很簡單,難道不是嗎?

接下來,讓我們討論下「processes」?!竝rocesses」究竟是什么呢?

本質(zhì)上說,一個(gè) generator 函數(shù)的作用相當(dāng)于虛擬的「進(jìn)程」。它是一段高度自控的程序,如果 JavaScript 允許的話,它能夠和程序中的其他代碼并行運(yùn)行。

說實(shí)話,上面有一點(diǎn)捏造事實(shí)了,如果 generator 函數(shù)能夠獲取到共享內(nèi)存中的值(也就是說,如果它能夠獲取到一些除它本身內(nèi)部的局部變量外的「自由變量」),那么它也就不那么獨(dú)立了。但是現(xiàn)在讓我們先假設(shè)我們擁有一個(gè) generator 函數(shù),它不會(huì)去獲取函數(shù)外部的變量(在函數(shù)式編程中通常稱之為「組合子」)。因此理論上 generator 函數(shù)可以在其自己的進(jìn)程中獨(dú)立運(yùn)行。

但是我們這兒所討論的是「processes」--復(fù)數(shù)形式--,因?yàn)楦匾氖俏覀儞碛袃蓚€(gè)或者多個(gè)的進(jìn)程。換句話說,兩個(gè)或者多個(gè) generator 函數(shù)通常會(huì)同時(shí)出現(xiàn)在我們的代碼中,然后協(xié)作完成一些更加復(fù)雜的任務(wù)。

為什么將 generator 函數(shù)拆分為多個(gè)而不是一個(gè)呢?最重要的原因:實(shí)現(xiàn)功能和關(guān)注點(diǎn)的解耦。如果你現(xiàn)在正在著手一項(xiàng) XYZ 的任務(wù),你將這個(gè)任務(wù)拆分成了一些子任務(wù),如 X, Y和 Z,并且每一個(gè)任務(wù)都通過一個(gè) generator 函數(shù)實(shí)現(xiàn),現(xiàn)在這樣的拆分和解耦使得你的代碼更加易懂且可維護(hù)性更高。

這個(gè)你將一個(gè)function XYZ()分解為三個(gè)函數(shù)X(),Y(),Z(),然后在X()函數(shù)中調(diào)用Y(),在Y()函數(shù)中調(diào)用Z()的動(dòng)機(jī)是一樣的,我們將一個(gè)函數(shù)分解成多個(gè)函數(shù),分離的代碼更加容易推理,同時(shí)也是的代碼可維護(hù)性增強(qiáng)。

我們可以通過多個(gè) generator 函數(shù)來完成相同的事情

最后,「communicating」。這有表達(dá)什么意思呢?他是從上面--協(xié)程—的概念中演進(jìn)而來,協(xié)程的意思也就是說多個(gè) generator 函數(shù)可能會(huì)相互協(xié)作,他們需要一個(gè)交流溝通的渠道(不僅僅是能夠從靜態(tài)作用域中獲取到共享的變量,同時(shí)是一個(gè)真實(shí)能夠分享溝通的渠道,所有的 generator 函數(shù)都能夠通過獨(dú)有的途徑與之交流)。

這個(gè)通信渠道有哪些作用呢?實(shí)際上不論你想發(fā)送什么數(shù)據(jù)(數(shù)字 number,字符串 strings 等),你實(shí)際上不需要通過渠道來實(shí)際發(fā)送消息來和渠道進(jìn)行通信?!窩ommunication」和協(xié)作一樣簡單,就和將控制權(quán)在不同 generator 函數(shù)之間傳遞一樣。

為什么需要傳遞控制權(quán)?最主要的原因是 JS是單線程的,在同一時(shí)間只允許一個(gè) generator 函數(shù)的執(zhí)行。其他 generator 函數(shù)處于運(yùn)行期間的暫停狀態(tài),也就是說這些暫停的 generator 函數(shù)都在其任務(wù)執(zhí)行過程中停了下來,僅僅是停了下來,等待著在必要的時(shí)候重新啟動(dòng)運(yùn)行。

這并不是說我們實(shí)現(xiàn)了(譯者注:作者的意思應(yīng)該是在沒有其他庫的幫助下)任意獨(dú)立的「進(jìn)程」可以魔法般的進(jìn)行協(xié)作和通信。

相反,顯而易見的是任意成功得 CSP 實(shí)現(xiàn)都是精心策劃的,將現(xiàn)有的問題領(lǐng)域進(jìn)行邏輯上的分解,每一塊在設(shè)計(jì)上都與其他塊協(xié)調(diào)工作。// TODO 這一段好難翻譯啊。

我關(guān)于 CSP 的理解也許完全錯(cuò)了,但是在實(shí)際過程中我并沒有看到兩個(gè)任意的 generator 函數(shù)能夠以某種方式膠合在一起成為一個(gè) CSP 模式,這兩個(gè) generator 函數(shù)必然需要某些特殊的設(shè)計(jì)才能夠相互的通信,比如雙方都遵守相同的通信協(xié)議等。

通過 JS 實(shí)現(xiàn) CSP 模式

在通過 JS 實(shí)現(xiàn) CSP 理論的過程中已經(jīng)有一些有趣的探索了。

上文我們提及的 David Nolen 有一些有趣的項(xiàng)目,包括 Om和 core.async ,Koa通過其use(..)方法對(duì) CSP 也有些有趣的嘗試。另外一個(gè)庫 js-csp完全忠實(shí)于 core.async/Go CSP API。

你應(yīng)該切實(shí)的去瀏覽下上述的幾個(gè)杰出的項(xiàng)目,去發(fā)現(xiàn)通過 JS實(shí)現(xiàn) CSP 的的不同途徑和實(shí)例的探討。

asynquence 中的 runner(..) 方法:為 CSP 而設(shè)計(jì)

由于我強(qiáng)烈地想要在我的 JS 代碼中運(yùn)用 CSP 模式,很自然地想到了擴(kuò)展我現(xiàn)有的異步控制流的庫asynquence ,為其添加 CSP 處理能力。

我已經(jīng)有了 runner(..)插件工具能夠幫助我異步運(yùn)行 generator 函數(shù)(參見第三篇文章Going Async With Generators),因此對(duì)于我來說,通過擴(kuò)展該方法使得其具有像CSP 形式一樣處理多個(gè) generator函數(shù)的能力變得相對(duì)容易很多。

首選我需要解決的設(shè)計(jì)問題:我怎樣知道下一個(gè)處理哪個(gè) generator 函數(shù)呢?

如果我們?cè)诿總€(gè) generator 函數(shù)上面添加類似 ID一樣的標(biāo)示,這樣別的 generator 函數(shù)就能夠很容易分清楚彼此,并且能夠準(zhǔn)確的將消息或者控制權(quán)傳遞給其他進(jìn)程,但是這種方法顯得累贅且冗余。經(jīng)過眾多嘗試后,我找到了一種簡便的方法,稱之為「循環(huán)調(diào)度法」。如果你要處理一組三個(gè)的 generator 函數(shù) A, B, C,A 首先獲得控制權(quán),當(dāng) A 調(diào)用 yield 表達(dá)式將控制權(quán)移交給 B,再后來 B 通過 yield 表達(dá)式將控制權(quán)移交給 C,一個(gè)循環(huán)后,控制權(quán)又重新回到了 A generator 函數(shù),如此往復(fù)。

但是我們究竟如何轉(zhuǎn)移控制權(quán)呢?是否需要一個(gè)明確的 API 來處理它呢?再次,經(jīng)過眾多嘗試后,我找到了一個(gè)更加明確的途徑,該方法和Koa 處理有些類似(完全是巧合):每一個(gè) generator 對(duì)同一個(gè)共享的「token」具有引用,yield表達(dá)式的作用僅僅是轉(zhuǎn)移控制權(quán)。

另外一個(gè)問題,消息渠道究竟應(yīng)該采取什么樣的形式呢。一端的頻譜就是你將看到和 core.async 和 js-csp(put(..take(..))相似的 API 設(shè)計(jì)。經(jīng)過我的嘗試后,我傾向于頻譜的另一端,你將看到一個(gè)不那么正式的途徑(甚至不是一個(gè) API,僅僅是共享一個(gè)像array一樣的數(shù)據(jù)結(jié)構(gòu)),但是它又是那么的合適且有效。

我決定使用一個(gè)數(shù)組(稱作messages)來作為消息渠道,你可以采取任意必要的數(shù)組方法來填充/消耗數(shù)組。你可以使用push()方法來想數(shù)組中推入消息,你也可以使用pop()方法來將消息從數(shù)組中推出,你也可以按照一些約定慣例想數(shù)組中插入不同的消息,這些消息也許是更加復(fù)雜的數(shù)據(jù)接口,等等。

我的疑慮是一些任務(wù)需要相當(dāng)簡單的消息來傳遞,而另外一些任務(wù)(消息)卻更加復(fù)雜,因此我沒有在這簡單的例子上面花費(fèi)過多的精力,而是選擇了不去對(duì) message 渠道進(jìn)行格式化,它就是簡簡單單的一個(gè)數(shù)組。(因此也就沒有為array本身設(shè)計(jì)特殊的 API)。同時(shí),在你覺得格式化消息渠道有用的時(shí)候,你也可以很容易的為該消息傳遞機(jī)制添加格外的格式化(參見下面的狀態(tài)機(jī)的事例)。

最后,我發(fā)現(xiàn)這些 generator 函數(shù)「進(jìn)程」依然受益于多帶帶的 generator 函數(shù)的異步能力。換句話說,如果你通過 yield 表達(dá)式不是傳遞的一個(gè)「control-token」,你通過 yield 表達(dá)式傳遞的一個(gè) Promise (或者異步序列),runner(..)的運(yùn)行機(jī)制會(huì)暫停并等待返回值,并且不會(huì)轉(zhuǎn)移控制權(quán)。他會(huì)將該返回值傳遞會(huì)當(dāng)前進(jìn)程(generator 函數(shù))并保持該控制權(quán)。

上面最后一點(diǎn)(如果我說明得正確的話)是和其他庫最具爭議的地方,從其他庫看來,真是的 CSP 模式在 yield 表達(dá)式執(zhí)行后移交控制權(quán),然而,我發(fā)現(xiàn)在我的庫中我這樣處理卻相當(dāng)有用。(譯者注:作者就是這樣自信)

一個(gè)簡單的 FooBar 例子

我們已經(jīng)理論充足了,讓我們看一些代碼:

// Note: omitting fictional `multBy20(..)` and
// `addTo2(..)` asynchronous-math functions, for brevity

function *foo(token) {
    // grab message off the top of the channel
    var value = token.messages.pop(); // 2

    // put another message onto the channel
    // `multBy20(..)` is a promise-generating function
    // that multiplies a value by `20` after some delay
    token.messages.push( yield multBy20( value ) );

    // transfer control
    yield token;

    // a final message from the CSP run
    yield "meaning of life: " + token.messages[0];
}

function *bar(token) {
    // grab message off the top of the channel
    var value = token.messages.pop(); // 40

    // put another message onto the channel
    // `addTo2(..)` is a promise-generating function
    // that adds value to `2` after some delay
    token.messages.push( yield addTo2( value ) );

    // transfer control
    yield token;
}

OK,上面出現(xiàn)了兩個(gè) generator「進(jìn)程」,*foo()*bar()。你會(huì)發(fā)現(xiàn)這兩個(gè)進(jìn)程都將操作token對(duì)象(當(dāng)然,你可以以你喜歡的方式稱呼它)。token對(duì)象上的messages屬性值就是我們的共享的消息渠道。我們可以在 CSP 初始化運(yùn)行的時(shí)候給它添加一些初始值。

yield token明確的將控制權(quán)轉(zhuǎn)一個(gè)「下一個(gè)」generator 函數(shù)(循環(huán)調(diào)度法)。然后yield multBy20(value)yield addTo2(value)兩個(gè)表達(dá)式都是傳遞的 promises(從上面虛構(gòu)的延遲數(shù)學(xué)計(jì)算方法),這也意味著,generator 函數(shù)將在該處暫停知道 promise 完成。當(dāng) promise 被解決后(fulfill 或者 reject),當(dāng)前掌管控制權(quán)的 generator 函數(shù)重新啟動(dòng)繼續(xù)執(zhí)行。

無論最終的 yield的值是什么,在我們的例子中yield "meaning of..."表達(dá)式的值,將是我們 CSP 執(zhí)行的最終返回?cái)?shù)據(jù)。

現(xiàn)在我們兩個(gè) CSP 模式的 generator 進(jìn)程,我們?cè)趺催\(yùn)行他們呢?當(dāng)然是使用 asynquence:

// start out a sequence with the initial message value of `2`
ASQ( 2 )

// run the two CSP processes paired together
.runner(
    foo,
    bar
)

// whatever message we get out, pass it onto the next
// step in our sequence
.val( function(msg){
    console.log( msg ); // "meaning of life: 42"
} );

很明顯,上面僅是一個(gè)無關(guān)緊要的例子,但是其也能足以很好的表達(dá) CSP 的概念了。

現(xiàn)在是時(shí)候去嘗試一下上面的例子(嘗試著修改下值)來搞明白這一概念的含義,進(jìn)而能夠編寫自己的 CSP 模式代碼。

另外一個(gè)「玩具」演示用例

如果那我們來看看最為經(jīng)典的 CSP 例子,但是希望大家從文章上面的解釋及發(fā)現(xiàn)來入手,而不是像通常情況一樣,從一些學(xué)術(shù)純化論者的觀點(diǎn)中導(dǎo)出。

Ping-pong。多么好玩的游戲,??!它也是我最喜歡的體育運(yùn)動(dòng)了。

讓我們想象一下,你已經(jīng)完全實(shí)現(xiàn)了打乒乓球游戲的代碼,你通過一個(gè)循環(huán)來運(yùn)行這個(gè)游戲,你有兩個(gè)片段的代碼(通常,通過if或者switch語句來進(jìn)行分支)來分別代表兩個(gè)玩家。

你的代碼運(yùn)行良好,并且你的游戲就像真是玩耍乒乓球一樣!

但是還記得為什么我說 CSP 模式是如此有用呢?它完成了關(guān)注點(diǎn)和功能模塊的分離。在上面的乒乓球游戲中我們?cè)趺捶蛛x的功能點(diǎn)呢?就是這兩位玩家!

因此,我們可以在一個(gè)比較高的層次上,通過兩個(gè)「進(jìn)程」(generator 函數(shù))來對(duì)我們的游戲建模,每個(gè)進(jìn)程代表一位玩家,我們還需要關(guān)注一些細(xì)節(jié)問題,我們很快就感覺到還需要一些「膠水代碼」來在兩位玩家之間進(jìn)行控制權(quán)的分配(交換),這些代碼可以作為第三個(gè) generator 函數(shù)進(jìn)程,我們可以稱之為裁判員。

我們已經(jīng)消除了所有可能會(huì)遇到的與專業(yè)領(lǐng)域相關(guān)的問題,比如得分,游戲機(jī)制,物理學(xué)常識(shí),游戲策略,電腦玩家,控制等。在我們的用例中我們只關(guān)心模擬玩耍乒乓球的反復(fù)往復(fù)的過程,(這一過程也正隱喻了 CSP 模式中的轉(zhuǎn)移控制權(quán))。

想要親自嘗試下演示用例?那就運(yùn)行把(注意:使用最新每夜版 FF 或者 Chrome,并且?guī)в兄С?ES6,來看看 generators 如何工作)

現(xiàn)在,讓我們來一段一段的閱讀代碼。

首先,asynquence 序列長什么樣呢?

ASQ(
    ["ping","pong"], // player names
    { hits: 0 } // the ball
)
.runner(
    referee,
    player,
    player
)
.val( function(msg){
    message( "referee", msg );
} );

我們給我們的序列設(shè)置了兩個(gè)初始值["ping", "pong"]{hits: 0}。我們將在后面討論它們。

接下來,我們?cè)O(shè)置 CSP 運(yùn)行 3 個(gè)進(jìn)程(協(xié)作程序):*referee() 和 兩個(gè)*player()實(shí)例。

游戲最后的消息傳遞給了我們序列的第二步,我們將在序列第二步中輸出裁判傳遞的消息。

裁判進(jìn)程的代碼實(shí)現(xiàn):

function *referee(table){
    var alarm = false;

    // referee sets an alarm timer for the game on
    // his stopwatch (10 seconds)
    setTimeout( function(){ alarm = true; }, 10000 );

    // keep the game going until the stopwatch
    // alarm sounds
    while (!alarm) {
        // let the players keep playing
        yield table;
    }

    // signal to players that the game is over
    table.messages[2] = "CLOSED";

    // what does the referee say?
    yield "Time"s up!";
}

我們稱「控制中token」為table,這正好和(乒乓球游戲)專業(yè)領(lǐng)域中的稱呼想一致,這是一個(gè)很好的語義化,一個(gè)游戲玩家通過用拍子將球「yields 傳遞 table」給另外一個(gè)玩家,難道不夠形象嗎?

while循環(huán)的作用就是在*referee()進(jìn)程中,只要警報(bào)器沒有吹響,他將不斷地通過 yield 表達(dá)式將 table 傳遞給玩家。當(dāng)警報(bào)器吹響,他掌管了控制權(quán),宣布游戲結(jié)束「時(shí)間到了」。

現(xiàn)在,讓我們來看看*player()generator 函數(shù)(在我們的代碼中我們兩次使用了該實(shí)例):

function *player(table) {
    var name = table.messages[0].shift();
    var ball = table.messages[1];

    while (table.messages[2] !== "CLOSED") {
        // hit the ball
        ball.hits++;
        message( name, ball.hits );

        // artificial delay as ball goes back to other player
        yield ASQ.after( 500 );

        // game still going?
        if (table.messages[2] !== "CLOSED") {
            // ball"s now back in other player"s court
            yield table;
        }
    }

    message( name, "Game over!" );
}

第一位玩家從消息數(shù)組中取得他的名字「ping」,然后,第二位玩家取得他的名字「pong」,這樣他們可以很好的分辨彼此的身份。兩位玩家同時(shí)共享ball這個(gè)對(duì)象的引用(通過他的hits計(jì)數(shù))。

只要玩家沒有從裁判口中聽到結(jié)束的消息,他們就將通過將計(jì)數(shù)器加一來「hit」ball(并且會(huì)輸入一條計(jì)數(shù)器消息),然后,等待500ms(僅僅是模擬乒乓球的飛行耗時(shí),不要還以為乒乓球以光速飛行呢)。

如果游戲依然進(jìn)行,游戲玩家「yield 傳遞 table」給另外一位玩家。

就是這樣!

查看一下演示用例的代碼獲取一份完整用例的代碼,看看不同代碼片段之間是如何協(xié)同工作的。

狀態(tài)機(jī):Generator 協(xié)同程序

最后一個(gè)例子,通過一個(gè) generator 函數(shù)集合組成的協(xié)同程序來定義一個(gè)狀態(tài)機(jī),這一協(xié)同程序都是通過一個(gè)簡單的工具函數(shù)來運(yùn)行的。

演示用例(注意:使用最新的每夜版 FF 或者 Chrome,并且支持 ES6的語法特性,看看 generator 函數(shù)如何工作)

首先讓我們來定義一個(gè)工具函數(shù),來幫助我們控制我們有限的狀態(tài):

function state(val, handler) {
    // make a coroutine handler (wrapper) for this state
    return function*(token) {
        // state transition handler
        function transition(to) {
            token.messages[0] = to;
        }

        // default initial state (if none set yet)
        if (token.messages.length < 1) {
            token.messages[0] = val;
        }

        // keep going until final state (false) is reached
        while (token.messages[0] !== false) {
            // current state matches this handler?
            if (token.messages[0] === val) {
                // delegate to state handler
                yield *handler( transition );
            }

            // transfer control to another state handler?
            if (token.messages[0] !== false) {
                yield token;
            }
        }
    };
}

state(..) 工具函數(shù)為一個(gè)特殊的狀態(tài)值創(chuàng)建了一個(gè)generator 代理的上層封裝,它將自動(dòng)的運(yùn)行狀態(tài)機(jī),并且在不同的狀態(tài)轉(zhuǎn)換下轉(zhuǎn)移控制權(quán)。

按照慣例來說,我已經(jīng)決定使用的token.messages[0]中的共享數(shù)據(jù)插槽來儲(chǔ)存狀態(tài)機(jī)的當(dāng)前狀態(tài)值,這也意味著你可以在序列的前一個(gè)步驟來對(duì)該狀態(tài)值進(jìn)行初始化,但是,如果沒有傳遞該初始化狀態(tài),我們簡單的在定義第一個(gè)狀態(tài)是將該狀態(tài)設(shè)置為初始狀態(tài)。同時(shí),按照慣例,最后終止的狀態(tài)值設(shè)置為false。正如你認(rèn)為合適,也很容易改變?cè)摖顟B(tài)。

狀態(tài)值可以是多種數(shù)據(jù)格式之一,數(shù)字,字符串等等,只要改數(shù)據(jù)可以通過嚴(yán)格的===來檢測相等性,你就可以使用它來作為狀態(tài)值。

在接下來的例子中,我展示了一個(gè)擁有四個(gè)數(shù)組狀態(tài)的狀態(tài)機(jī),并且其運(yùn)行運(yùn)行:1 -> 4 -> 3 -> 2。該順序僅僅為了演示所需,我們使用了一個(gè)計(jì)數(shù)器來幫助我們?cè)诓煌瑺顟B(tài)間能夠多次傳遞,當(dāng)我們的 generator 狀態(tài)機(jī)最終遇到了終止?fàn)顟B(tài)false時(shí),異步序列運(yùn)行至下一個(gè)步驟,正如你所期待那樣。

// counter (for demo purposes only)
var counter = 0;

ASQ( /* optional: initial state value */ )

// run our state machine, transitions: 1 -> 4 -> 3 -> 2
.runner(

    // state `1` handler
    state( 1, function*(transition){
        console.log( "in state 1" );
        yield ASQ.after( 1000 ); // pause state for 1s
        yield transition( 4 ); // goto state `4`
    } ),

    // state `2` handler
    state( 2, function*(transition){
        console.log( "in state 2" );
        yield ASQ.after( 1000 ); // pause state for 1s

        // for demo purposes only, keep going in a
        // state loop?
        if (++counter < 2) {
            yield transition( 1 ); // goto state `1`
        }
        // all done!
        else {
            yield "That"s all folks!";
            yield transition( false ); // goto terminal state
        }
    } ),

    // state `3` handler
    state( 3, function*(transition){
        console.log( "in state 3" );
        yield ASQ.after( 1000 ); // pause state for 1s
        yield transition( 2 ); // goto state `2`
    } ),

    // state `4` handler
    state( 4, function*(transition){
        console.log( "in state 4" );
        yield ASQ.after( 1000 ); // pause state for 1s
        yield transition( 3 ); // goto state `3`
    } )

)

// state machine complete, so move on
.val(function(msg){
    console.log( msg );
});

上面代碼的運(yùn)行機(jī)制是不是非常簡單。

yield ASQ.after(1000)表示這些 generator 函數(shù)可以進(jìn)行 promise/sequence等異步工作,正如我們先前縮減,yield transition(..)告訴我們?cè)鯓訉⒖刂茩?quán)傳遞給下一個(gè)狀態(tài)。

我們的state(..)工具函數(shù)真實(shí)的完成了yield *代理這一艱難的工作,像變戲法一樣,使得我們能夠以一種簡單自然的形式來對(duì)狀態(tài)進(jìn)行操控。

總結(jié)

CSP 模式的關(guān)鍵點(diǎn)在于將兩個(gè)或者多個(gè) generator「進(jìn)程」組合在一起,并為他們提供一個(gè)共享的通信渠道,和一個(gè)在其彼此之間傳遞控制權(quán)的方法。

市面上已經(jīng)有很多庫多多少少實(shí)現(xiàn)了GO 和 Clojure/ClojureScript APIs 相同或者相同語義的 CSP 模式。在這些庫的背后是一些聰明而富有創(chuàng)造力的開發(fā)者門,這些庫的出現(xiàn),也意味著需要更大的資源投入以及研究。

asynquence 嘗試著通過著通過不那么正式的方法卻依然希望給大家呈現(xiàn) CSP 的運(yùn)行機(jī)制,只不過,asynquence 的runner(..)方法使得了我們通過 generator 模擬 CSP 模式變得如此簡單,正如你在本篇文章所學(xué)的那樣。

asynquence CSP 模式中最為出色的部分就是你將所有的異步處理手段(promise,generators,flow control 等)以及剩下的有機(jī)的組合在了一起,你不同異步處理結(jié)合在一起,因此你可以任何合適的手段來處理你的任務(wù),而且,都在同一個(gè)小小的庫中。

現(xiàn)在,在結(jié)束該系列最后一篇文章后,我們已經(jīng)完成了對(duì) generator 函數(shù)詳盡的研究,我所希望的是你能夠在閱讀這些文章后有所啟發(fā),并對(duì)你現(xiàn)有的代碼進(jìn)行一次徹底革命!你將會(huì)用 generator 函數(shù)創(chuàng)造什么奇跡呢?

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/84814.html

相關(guān)文章

  • es6 Generators詳解

    摘要:每個(gè)任務(wù)必須顯式地掛起自己,在任務(wù)切換發(fā)生時(shí)給予它完全的控制。在這些嘗試中,數(shù)據(jù)經(jīng)常在任務(wù)之間共享。但由于明確的暫停,幾乎沒有風(fēng)險(xiǎn)。 翻譯自 github 概述 什么是generators? 我們可以把generators理解成一段可以暫停并重新開始執(zhí)行的函數(shù) function* genFunc() { // (A) console.log(First); yi...

    zhaot 評(píng)論0 收藏0
  • 關(guān)于協(xié)程和 ES6 中的 Generator

    摘要:關(guān)于協(xié)程和中的什么是協(xié)程進(jìn)程和線程眾所周知,進(jìn)程和線程都是一個(gè)時(shí)間段的描述,是工作時(shí)間段的描述,不過是顆粒大小不同,進(jìn)程是資源分配的最小單位,線程是調(diào)度的最小單位。子程序就是協(xié)程的一種特例。 關(guān)于協(xié)程和 ES6 中的 Generator 什么是協(xié)程? 進(jìn)程和線程 眾所周知,進(jìn)程和線程都是一個(gè)時(shí)間段的描述,是CPU工作時(shí)間段的描述,不過是顆粒大小不同,進(jìn)程是 CPU 資源分配的最小單位,...

    RyanHoo 評(píng)論0 收藏0
  • 2017年1月前端月報(bào)

    摘要:平日學(xué)習(xí)接觸過的網(wǎng)站積累,以每月的形式發(fā)布。年以前看這個(gè)網(wǎng)址概況在線地址前端開發(fā)群月報(bào)提交原則技術(shù)文章新的為主。 平日學(xué)習(xí)接觸過的網(wǎng)站積累,以每月的形式發(fā)布。2017年以前看這個(gè)網(wǎng)址:http://www.kancloud.cn/jsfron... 概況 在線地址:http://www.kancloud.cn/jsfront/month/82796 JS前端開發(fā)群月報(bào) 提交原則: 技...

    FuisonDesign 評(píng)論0 收藏0
  • 2017年1月前端月報(bào)

    摘要:平日學(xué)習(xí)接觸過的網(wǎng)站積累,以每月的形式發(fā)布。年以前看這個(gè)網(wǎng)址概況在線地址前端開發(fā)群月報(bào)提交原則技術(shù)文章新的為主。 平日學(xué)習(xí)接觸過的網(wǎng)站積累,以每月的形式發(fā)布。2017年以前看這個(gè)網(wǎng)址:http://www.kancloud.cn/jsfron... 概況 在線地址:http://www.kancloud.cn/jsfront/month/82796 JS前端開發(fā)群月報(bào) 提交原則: 技...

    ivyzhang 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<