成人无码视频,亚洲精品久久久久av无码,午夜精品久久久久久毛片,亚洲 中文字幕 日韩 无码

資訊專(zhuān)欄INFORMATION COLUMN

通過(guò)ES6 Generator函數(shù)實(shí)現(xiàn)異步流程

劉厚水 / 2473人閱讀

摘要:換句話說(shuō),我們很好的對(duì)代碼的功能關(guān)注點(diǎn)進(jìn)行了分離通過(guò)將使用消費(fèi)值得地方函數(shù)中的邏輯和通過(guò)異步流程來(lái)獲取值迭代器的方法進(jìn)行了有效的分離。但是現(xiàn)在我們通過(guò)來(lái)管理代碼的異步流程部分,我們解決了回調(diào)函數(shù)所帶來(lái)的反轉(zhuǎn)控制等問(wèn)題。

本文翻譯自 Going Async With ES6 Generators

由于個(gè)人能力知識(shí)有限,翻譯過(guò)程中難免有紕漏和錯(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)對(duì)ES6 generators有了初步了解并且能夠方便的使用它,是時(shí)候準(zhǔn)備將其運(yùn)用到真實(shí)項(xiàng)目中提高現(xiàn)有代碼質(zhì)量。

Generator函數(shù)的強(qiáng)大在于允許你通過(guò)一些實(shí)現(xiàn)細(xì)節(jié)來(lái)將異步過(guò)程隱藏起來(lái),依然使代碼保持一個(gè)單線程、同步語(yǔ)法的代碼風(fēng)格。這樣的語(yǔ)法使得我們能夠很自然的方式表達(dá)我們程序的步驟/語(yǔ)句流程,而不需要同時(shí)去操作一些異步的語(yǔ)法格式。

換句話說(shuō),我們很好的對(duì)代碼的功能/關(guān)注點(diǎn)進(jìn)行了分離:通過(guò)將使用(消費(fèi))值得地方(generator函數(shù)中的邏輯)和通過(guò)異步流程來(lái)獲取值(generator迭代器的next()方法)進(jìn)行了有效的分離。

結(jié)果就是?不僅我們的代碼具有強(qiáng)大的異步能力, 同時(shí)又保持了可讀性和可維護(hù)性的同步語(yǔ)法的代碼風(fēng)格。

那么我們?cè)趺磳?shí)現(xiàn)這些功能呢?

最簡(jiǎn)單的異步實(shí)現(xiàn)

最簡(jiǎn)單的情況,generator函數(shù)不需要額外的代碼來(lái)處理異步功能,因?yàn)槟愕某绦蛞膊恍枰@樣做。

例如,讓我們假象你已經(jīng)寫(xiě)下了如下代碼:

function makeAjaxCall(url,cb) {
    // do some ajax fun
    // call `cb(result)` when complete
}

makeAjaxCall( "http://some.url.1", function(result1){
    var data = JSON.parse( result1 );

    makeAjaxCall( "http://some.url.2/?id=" + data.id, function(result2){
        var resp = JSON.parse( result2 );
        console.log( "The value you asked for: " + resp.value );
    });
} );

通過(guò)generator函數(shù)(不帶任何其他裝飾)來(lái)實(shí)現(xiàn)和上面代碼相同的功能,實(shí)現(xiàn)代碼如下:

function request(url) {
    // this is where we"re hiding the asynchronicity,
    // away from the main code of our generator
    // `it.next(..)` is the generator"s iterator-resume
    // call
    makeAjaxCall( url, function(response){
        it.next( response );
    } );
    // Note: nothing returned here!
}

function *main() {
    var result1 = yield request( "http://some.url.1" );
    var data = JSON.parse( result1 );

    var result2 = yield request( "http://some.url.2?id=" + data.id );
    var resp = JSON.parse( result2 );
    console.log( "The value you asked for: " + resp.value );
}

var it = main();
it.next(); // get it all started

讓我來(lái)解釋下上面代碼是如何工作的。

request(..)幫助函數(shù)主要對(duì)普通的makeAjaxCall(..)實(shí)用函數(shù)進(jìn)行包裝,保證在在其回調(diào)函數(shù)中調(diào)用generator迭代器的next(..)方法。

在調(diào)用request(..)的過(guò)程中,你可能已經(jīng)發(fā)現(xiàn)函數(shù)并沒(méi)有顯式的返回值(換句話說(shuō),其返回undefined)。這沒(méi)有什么大不了的,但是與本文后面的方法相比,返回值就顯得比較重要了。這兒我們生效的yield undefined

當(dāng)我們代碼執(zhí)行到yield..時(shí)(yield表達(dá)式返回undefined值),我們僅僅在這一點(diǎn)暫停了我們的generator函數(shù)而沒(méi)有做其他任何事。等待著it.next(..)方法的執(zhí)行來(lái)重新啟動(dòng)該generator函數(shù),而it.next()方法是在Ajax獲取數(shù)據(jù)結(jié)束后的回調(diào)函數(shù)(推入異步隊(duì)列等待執(zhí)行)中執(zhí)行的。

我們對(duì)yield..表達(dá)式的結(jié)果做了什么呢?我們將其結(jié)果賦值給了變量result1。那么我們是怎么將Ajax請(qǐng)求結(jié)果放到該yield..表達(dá)式的返回值中的呢?

因?yàn)楫?dāng)我們?cè)贏jax的回調(diào)函數(shù)中調(diào)用it.next(..)方法的時(shí)候,我們將Ajax的返回值作為參數(shù)傳遞給next(..)方法,這意味著該Ajax返回值傳遞到了generator函數(shù)內(nèi)部,當(dāng)前函數(shù)內(nèi)部暫停的位置,也就是result1 = yield..語(yǔ)句中部。

上面的代碼真的很酷并且強(qiáng)大。本質(zhì)上,result1 = yield request(..)作用是用來(lái)請(qǐng)求值,但是請(qǐng)求的過(guò)程幾乎完全對(duì)我們不可見(jiàn)- -或者至少在此處我們不用怎么擔(dān)心它 - - 因?yàn)榈讓拥膶?shí)現(xiàn)使得該步驟成為了異步操作。generator函數(shù)通過(guò)通過(guò)在yield表達(dá)式中隱藏的暫停功能以及將重新啟動(dòng)generator函數(shù)的功能分離到另外一個(gè)函數(shù)中,來(lái)實(shí)現(xiàn)了異步操作。因此在主要代碼中我們通過(guò)一個(gè)同步的代碼風(fēng)格來(lái)請(qǐng)求值。

第二句result2 = yield result()(譯者注:作者的筆誤,應(yīng)該是result2 = yield request(..))代碼,和上面的代碼工作原理幾乎無(wú)異:通過(guò)明顯的暫停和重新啟動(dòng)機(jī)制來(lái)獲取到我們請(qǐng)求的數(shù)據(jù),而在generator函數(shù)內(nèi)部我們不用再為一些異步代碼細(xì)節(jié)為煩惱。

當(dāng)然,yield的出現(xiàn),也就微妙的暗示一些神奇(??!異步)的事情可能在此處發(fā)生。和嵌套回調(diào)函數(shù)帶來(lái)的回調(diào)地獄相比,yield在語(yǔ)法層面上優(yōu)于回調(diào)函數(shù)(甚至在API上優(yōu)于promise的鏈?zhǔn)秸{(diào)用)。

需要注意上面我說(shuō)的是“可能”。generator函數(shù)完成上面的工作,這本身就是一件非常強(qiáng)大的事情。上面的程序始終發(fā)送一個(gè)異步的Ajax請(qǐng)求,假如不發(fā)送異步Ajax請(qǐng)求呢?倘若我們改變我們的程序來(lái)從緩存中獲取到先前(或者預(yù)先請(qǐng)求)Ajax請(qǐng)求的結(jié)果?或者從我們的URL路由中獲取數(shù)據(jù)來(lái)立刻fulfillAjax請(qǐng)求,而不用真正的向后端請(qǐng)求數(shù)據(jù)。

我們可以改變我們的request(..)函數(shù)來(lái)滿足上面的需求,如下:

var cache = {};

function request(url) {
    if (cache[url]) {
        // "defer" cached response long enough for current
        // execution thread to complete
        setTimeout( function(){
            it.next( cache[url] );
        }, 0 );
    }
    else {
        makeAjaxCall( url, function(resp){
            cache[url] = resp;
            it.next( resp );
        } );
    }
}

注意:在上面的代碼中我們使用了一個(gè)細(xì)微的技巧setTimeout(..0),當(dāng)從緩存中獲取結(jié)果時(shí)來(lái)延遲代碼的執(zhí)行。如果我們不延遲而是立即執(zhí)行it.next(..)方法,這將會(huì)導(dǎo)致錯(cuò)誤的發(fā)生,因?yàn)椋ㄟ@就是技巧所在)此時(shí)generator函數(shù)還沒(méi)有停止執(zhí)行。首先我們執(zhí)行request(..)函數(shù),然后通過(guò)yield來(lái)暫停generator函數(shù)。因此不能夠在request(..)函數(shù)中立即調(diào)用it.next(..)方法,因?yàn)樵诖藭r(shí),generator函數(shù)依然在運(yùn)行(yield 還沒(méi)有被調(diào)用)。但是我們可以在當(dāng)前線程運(yùn)行結(jié)束后,立即執(zhí)行it.next(..)。這就是setTimeout(..0)將要完成的工作。在文章后面我們將看到一個(gè)更加完美的解答。

現(xiàn)在,我們generator函數(shù)內(nèi)部主要代碼依然如下:

var result1 = yield request( "http://some.url.1" );
var data = JSON.parse( result1 );
..

看到?jīng)]?。?/strong>當(dāng)我們代碼從沒(méi)有緩存到上面有緩存的版本,我們generator函數(shù)內(nèi)部邏輯(我們的控制流程)竟然沒(méi)有變化。

*main()函數(shù)內(nèi)部代碼依然是請(qǐng)求數(shù)據(jù),暫停generator函數(shù)的執(zhí)行來(lái)等待數(shù)據(jù)的返回,數(shù)據(jù)傳回后繼續(xù)執(zhí)行。在我們當(dāng)前場(chǎng)景中,這個(gè)暫停可能相對(duì)比較長(zhǎng)(真實(shí)的向服務(wù)器發(fā)送請(qǐng)求,這可能會(huì)耗時(shí)300~800ms)或者幾乎立即執(zhí)行(使用setTimeout(..0)手段延遲執(zhí)行)。但是我們*main函數(shù)中的控制流程不用關(guān)心數(shù)據(jù)從何而來(lái)。

這就是從實(shí)現(xiàn)細(xì)節(jié)中將異步流程分離出來(lái)的強(qiáng)大力量。

更好的異步編程

利用上面提及的方法(回調(diào)函數(shù)),generators函數(shù)能夠完成一些簡(jiǎn)單的異步工作。但是卻相當(dāng)局限,因此我們需要一個(gè)更加強(qiáng)大的異步機(jī)制來(lái)與我們的generator函數(shù)匹配結(jié)合。完成一些更加繁重的異步流程。什么異步機(jī)制呢?Promises。

如果你依然對(duì)ES6 Promises感到困惑,我寫(xiě)過(guò)關(guān)于Promise的系列文章。去閱讀一下。我會(huì)等待你回來(lái),<滴答,滴答>。老掉牙的異步笑話了。

先前的Ajax代碼例子依然存在反轉(zhuǎn)控制的問(wèn)題(啊,回調(diào)地獄)正如文章最初的嵌套回調(diào)函數(shù)例子一樣。到目前為止,我們應(yīng)該已經(jīng)明顯察覺(jué)到了上面的例子存在一些待完善的地方:

到目前為止沒(méi)有明確的錯(cuò)誤處理機(jī)制,正如我們上一篇學(xué)習(xí)的文章,在發(fā)送Ajax請(qǐng)求的過(guò)程中我們可能檢測(cè)到錯(cuò)誤(在某處),通過(guò)it.throw(..)方法將錯(cuò)誤傳遞會(huì)generator函數(shù),然后在generator函數(shù)內(nèi)部通過(guò)try..catch模塊來(lái)處理該錯(cuò)誤。但是,我們?cè)凇昂竺妗睂⒁謩?dòng)處理更多工作(更多的代碼來(lái)處理我們的generator迭代器),如果在我們的程序中多次使用generators函數(shù),這些錯(cuò)誤處理代碼很難被復(fù)用。

如果makeAjaxCall(..)工具函數(shù)不受我們控制,碰巧它多次調(diào)用了回調(diào)函數(shù),或者同時(shí)將成功值或者錯(cuò)誤返回到generator函數(shù)中,等等。我們的generator函數(shù)就將變得極難控制(未捕獲的錯(cuò)誤,意外的返回值等)。處理、阻止上述問(wèn)題的發(fā)生很多都是一些重復(fù)的工作,同時(shí)也都不是輕輕松松能夠完成的。

很多時(shí)候我們需要同時(shí)并行處理多個(gè)任務(wù)(例如兩個(gè)并行的Ajax請(qǐng)求)。由于generator函數(shù)中的yield表達(dá)式執(zhí)行后都會(huì)暫停函數(shù)的執(zhí)行,不能夠同時(shí)運(yùn)行兩個(gè)或多個(gè)yield表達(dá)式,也就是說(shuō)yield表達(dá)式只能按順序一個(gè)接一個(gè)的運(yùn)行。因此在沒(méi)有大量手寫(xiě)代碼的前提下,一個(gè)yield表達(dá)式中同時(shí)執(zhí)行多個(gè)任務(wù)依然不太明朗。

正如你所見(jiàn),上面的所有問(wèn)題都可以被解決,但是又有誰(shuí)愿意每次重復(fù)手寫(xiě)這些代碼呢?我們需要一種更加強(qiáng)大的模式,該模式是可信賴(lài)且高度復(fù)用的,并且能夠很好的解決generator函數(shù)處理異步流程問(wèn)題。

什么模式?yield 表達(dá)式內(nèi)部是promise,當(dāng)這些promise被fulfill后重新啟動(dòng)generator函數(shù)。

回憶上面代碼,我們使用yield request(..),但是request(..)工具函數(shù)并沒(méi)有返回任何值,那么它僅僅yield undefined嗎?

讓我們稍微調(diào)整下上面的代碼。我們把request(..)函數(shù)改為以promise為基礎(chǔ)的函數(shù),因此該函數(shù)返回一個(gè)promise,現(xiàn)在我們通過(guò)yield表達(dá)式返回了一個(gè)真實(shí)的promise(而不是undefined)。

function request(url) {
    // Note: returning a promise now!
    return new Promise( function(resolve,reject){
        makeAjaxCall( url, resolve );
    } );
}

request(..)函數(shù)通過(guò)構(gòu)建一個(gè)promise來(lái)監(jiān)聽(tīng)Ajax的完成并且resolve返回值,并且返回該promise,因此promise也能夠被yield傳遞到generator函數(shù)外部,接下來(lái)呢?

我們需要一個(gè)工具函數(shù)來(lái)控制generator函數(shù)的迭代器,該工具函數(shù)接收yield表達(dá)式傳遞出來(lái)的promise,然后在promie 狀態(tài)轉(zhuǎn)為fulfill或者reject時(shí),通過(guò)迭代器的next(..)方法重新啟動(dòng)generator函數(shù)?,F(xiàn)在我為這個(gè)工具函數(shù)取名runGenerator(..):

// run (async) a generator to completion
// Note: simplified approach: no error handling here
function runGenerator(g) {
    var it = g(), ret;

    // asynchronously iterate over generator
    (function iterate(val){
        ret = it.next( val );

        if (!ret.done) {
            // poor man"s "is it a promise?" test
            if ("then" in ret.value) {
                // wait on the promise
                ret.value.then( iterate );
            }
            // immediate value: just send right back in
            else {
                // avoid synchronous recursion
                setTimeout( function(){
                    iterate( ret.value );
                }, 0 );
            }
        }
    })();
}

需要注意的關(guān)鍵點(diǎn):

我們自動(dòng)的初始化了generator函數(shù)(創(chuàng)建了it迭代器),然后我們異步運(yùn)行it來(lái)完成generator函數(shù)的執(zhí)行(done: true)。

我們尋找被yield表達(dá)式傳遞出來(lái)的promise(啊,也就是執(zhí)行it.next(..)方法后返回的對(duì)象中的value字段)。如此,我們通過(guò)在promise的then(..)方法中注冊(cè)函數(shù)來(lái)監(jiān)聽(tīng)器完成。

如果一個(gè)非promise值被傳遞出來(lái),我們僅僅將該值原樣返回到generator函數(shù)內(nèi)部,因此看上去立即重新啟動(dòng)了generator函數(shù)。

現(xiàn)在我們?cè)趺词褂盟兀?/p>

runGenerator( function *main(){
    var result1 = yield request( "http://some.url.1" );
    var data = JSON.parse( result1 );

    var result2 = yield request( "http://some.url.2?id=" + data.id );
    var resp = JSON.parse( result2 );
    console.log( "The value you asked for: " + resp.value );
} );

騙人!等等...上面代碼和更早的代碼幾乎完全一樣?哈哈,generator函數(shù)再次向我們炫耀了它的強(qiáng)大之處。實(shí)際上我們創(chuàng)建了promise,通過(guò)yield將其傳遞出去,然后重新啟動(dòng)generator函數(shù),直到函數(shù)執(zhí)行完成- - 所有被""隱藏""的實(shí)現(xiàn)細(xì)節(jié)!實(shí)際上并沒(méi)有隱藏起來(lái),只是和我們消費(fèi)該異步流程的代碼(generator中的控制流程)隔離開(kāi)來(lái)了。

通過(guò)等待yield出去的promise的完成,然后將fulfill的值通過(guò)it.next(..)方法傳遞回函數(shù)中,result1 = yield request(..)表達(dá)式就回獲取到正如先前一樣的請(qǐng)求值。

但是現(xiàn)在我們通過(guò)promises來(lái)管理generator代碼的異步流程部分,我們解決了回調(diào)函數(shù)所帶來(lái)的反轉(zhuǎn)控制等問(wèn)題。通過(guò)generator+promises的模式我們“免費(fèi)”解決上述所遇到的問(wèn)題:

現(xiàn)在我們用易用的內(nèi)部錯(cuò)誤處理機(jī)制。在runGenerator(..)函數(shù)中我們并沒(méi)有提及,但是監(jiān)聽(tīng)promise的錯(cuò)誤并非難事,我們只需通過(guò)it.throw(..)方法將promise捕獲的錯(cuò)誤拋進(jìn)generator函數(shù)內(nèi)部,在函數(shù)內(nèi)部通過(guò)try...catch模塊進(jìn)行錯(cuò)誤捕獲及處理。

promise給我們提供了可控性/可依賴(lài)性。不用擔(dān)心,也不用疑惑。

Promises擁有一些強(qiáng)大的抽象工具方法,利用這些方法可以自動(dòng)處理一些復(fù)雜的“并行”任務(wù)等。

例如,yield Prmise.all([ .. ])可以接受一個(gè)promise數(shù)組然后“并行”執(zhí)行這些任務(wù),然后yield出去一個(gè)多帶帶的promise(給generator函數(shù)處理),該promise將會(huì)等待所有并行的promise都完成后才被完成,你可以通過(guò)yield表達(dá)式的返回?cái)?shù)組(當(dāng)promise完成后)來(lái)獲取到所有并行promise的結(jié)果。數(shù)組中的結(jié)果和并行promises任務(wù)一一對(duì)應(yīng)(因此其完全忽略promise完成的順序)。

首先,讓我們研究下錯(cuò)誤處理:

// assume: `makeAjaxCall(..)` now expects an "error-first style" callback (omitted for brevity)
// assume: `runGenerator(..)` now also handles error handling (omitted for brevity)

function request(url) {
    return new Promise( function(resolve,reject){
        // pass an error-first style callback
        makeAjaxCall( url, function(err,text){
            if (err) reject( err );
            else resolve( text );
        } );
    } );
}

runGenerator( function *main(){
    try {
        var result1 = yield request( "http://some.url.1" );
    }
    catch (err) {
        console.log( "Error: " + err );
        return;
    }
    var data = JSON.parse( result1 );

    try {
        var result2 = yield request( "http://some.url.2?id=" + data.id );
    } catch (err) {
        console.log( "Error: " + err );
        return;
    }
    var resp = JSON.parse( result2 );
    console.log( "The value you asked for: " + resp.value );
} );

當(dāng)再URL 請(qǐng)求發(fā)出后一個(gè)promise被reject后(或者其他的錯(cuò)誤或異常),這個(gè)promise的reject值將會(huì)映射到一個(gè)generator函數(shù)錯(cuò)誤(通過(guò)runGenerator(..)內(nèi)部隱式的it.throw(..)來(lái)傳遞錯(cuò)誤),該錯(cuò)誤將會(huì)被try..catch模塊捕獲。

現(xiàn)在,讓我們看一個(gè)通過(guò)promises來(lái)管理更加錯(cuò)綜復(fù)雜的異步流程的事例:

function request(url) {
    return new Promise( function(resolve,reject){
        makeAjaxCall( url, resolve );
    } )
    // do some post-processing on the returned text
    .then( function(text){
        // did we just get a (redirect) URL back?
        if (/^https?://.+/.test( text )) {
            // make another sub-request to the new URL
            return request( text );
        }
        // otherwise, assume text is what we expected to get back
        else {
            return text;
        }
    } );
}

runGenerator( function *main(){
    var search_terms = yield Promise.all( [
        request( "http://some.url.1" ),
        request( "http://some.url.2" ),
        request( "http://some.url.3" )
    ] );

    var search_results = yield request(
        "http://some.url.4?search=" + search_terms.join( "+" )
    );
    var resp = JSON.parse( search_results );

    console.log( "Search results: " + resp.value );
} );

Promise.all([ .. ])會(huì)構(gòu)建一個(gè)新的promise來(lái)等待其內(nèi)部的三個(gè)并行promise的完成,該新的promise將會(huì)被yield表達(dá)式傳遞到外部給runGenerator(..)工具函數(shù)中,runGenerator()函數(shù)監(jiān)聽(tīng)該新生成的promise的完成,以便重新啟動(dòng)generator函數(shù)。并行的promise的返回值可能會(huì)成為另外一個(gè)URL的組成部分,然后通過(guò)yield表達(dá)式將另外一個(gè)promise傳遞到外部。關(guān)于更多的promise鏈?zhǔn)秸{(diào)用,參見(jiàn)文章

promise可以處理任何復(fù)雜的異步過(guò)程,你可以通過(guò)generator函數(shù)yield出去promises(或者promise返回promise)來(lái)獲取到同步代碼的語(yǔ)法形式。(對(duì)于promise或者generator兩個(gè)ES6的新特性,他們的結(jié)合或許是最好的模式)

runGenerator(..): 實(shí)用函數(shù)庫(kù)

在上面我們已經(jīng)定義了runGenerator(..)工具函數(shù)來(lái)順利幫助我們充分發(fā)揮generator+promise模式的卓越能力。我們甚至省略了(為了簡(jiǎn)略起見(jiàn))該工具函數(shù)的完整實(shí)現(xiàn),在錯(cuò)誤處理方面依然有些細(xì)微細(xì)節(jié)我們需要處理。

但是,你不愿意實(shí)現(xiàn)一個(gè)你自己的runGenerator(..)是嗎?

我不這么認(rèn)為。

許多promise/async庫(kù)都提供了上述工具函數(shù)。在此我不會(huì)一一論述,但是你一個(gè)查閱Q.spawn(..)co(..)庫(kù),等等。

但是我會(huì)簡(jiǎn)要的闡述我自己的庫(kù)asynquence中的runner(..)插件,相對(duì)于其他庫(kù),我想提供一些獨(dú)一無(wú)二的特性。如果對(duì)此感興趣并想學(xué)習(xí)更多關(guān)于asynquence的知識(shí)而不是淺嘗輒止,可以看看以前的兩篇文章深入asynquence

首先,asynquence提供了自動(dòng)處理上面代碼片段中的”error-first-style“回調(diào)函數(shù)的工具函數(shù):

function request(url) {
    return ASQ( function(done){
        // pass an error-first style callback
        makeAjaxCall( url, done.errfcb );
    } );
}

是不是看起來(lái)更加好看,不是嗎???

接下來(lái),asynquence提供了runner(..)插件來(lái)在異步序列(異步流程)中執(zhí)行g(shù)enerator函數(shù),因此你可以在runner前面的步驟傳遞信息到generator函數(shù)內(nèi),同時(shí)generator函數(shù)也可以傳遞消息出去到下一個(gè)步驟中,同時(shí)如你所愿,所有的錯(cuò)誤都自動(dòng)冒泡被最后的or所捕獲。

// first call `getSomeValues()` which produces a sequence/promise,
// then chain off that sequence for more async steps
getSomeValues()

// now use a generator to process the retrieved values
.runner( function*(token){
    // token.messages will be prefilled with any messages
    // from the previous step
    var value1 = token.messages[0];
    var value2 = token.messages[1];
    var value3 = token.messages[2];

    // make all 3 Ajax requests in parallel, wait for
    // all of them to finish (in whatever order)
    // Note: `ASQ().all(..)` is like `Promise.all(..)`
    var msgs = yield ASQ().all(
        request( "http://some.url.1?v=" + value1 ),
        request( "http://some.url.2?v=" + value2 ),
        request( "http://some.url.3?v=" + value3 )
    );

    // send this message onto the next step
    yield (msgs[0] + msgs[1] + msgs[2]);
} )

// now, send the final result of previous generator
// off to another request
.seq( function(msg){
    return request( "http://some.url.4?msg=" + msg );
} )

// now we"re finally all done!
.val( function(result){
    console.log( result ); // success, all done!
} )

// or, we had some error!
.or( function(err) {
    console.log( "Error: " + err );
} );

asyquence的runner(..)工具接受上一步序列傳遞下來(lái)的值(也有可能沒(méi)有值)來(lái)啟動(dòng)generator函數(shù),可以通過(guò)token.messages數(shù)組來(lái)獲取到傳入的值。

然后,和上面我們所描述的runGenerator(..)工具函數(shù)類(lèi)似,runner(..)也會(huì)監(jiān)聽(tīng)yield一個(gè)promise或者yield一個(gè)asynquence序列(在本例中,是指通過(guò)ASQ().all()方法生成的”并行”任務(wù)),然后等待promise或者asynquence序列的完成后重新啟動(dòng)generator函數(shù)。

當(dāng)generator函數(shù)執(zhí)行完成后,最后通過(guò)yield表達(dá)式傳遞的值將作為參數(shù)傳遞到下一個(gè)序列步驟中。

最后,如果在某個(gè)序列步驟中出現(xiàn)錯(cuò)誤,甚至在generator內(nèi)部,錯(cuò)誤都會(huì)冒泡到被注冊(cè)的or(..)方法中進(jìn)行錯(cuò)誤處理。

asynquence通過(guò)盡可能簡(jiǎn)單的方式來(lái)混合匹配promises和generator。你可以自由的在以promise為基礎(chǔ)的序列流程后面接generator控制流程,正如上面代碼。

ES7 async

在ES7的時(shí)間軸上有一個(gè)提案,并且有極大可能被接受,該提案將在JavaScript中添加另外一個(gè)函數(shù)類(lèi)型:async函數(shù),該函數(shù)相當(dāng)于用類(lèi)似于runGenerator(..)(或者asynquence的runner(..))工具函數(shù)在generator函數(shù)外部包裝一下,來(lái)使得其自動(dòng)執(zhí)行。通過(guò)async函數(shù),你可以把promises傳遞到外部然后async函數(shù)在promises狀態(tài)變?yōu)閒ulfill時(shí)自動(dòng)重新啟動(dòng)直到函數(shù)執(zhí)行完成。(甚至不需要復(fù)雜的迭代器參與)

async函數(shù)大概形式如下:

async function main() {
    var result1 = await request( "http://some.url.1" );
    var data = JSON.parse( result1 );

    var result2 = await request( "http://some.url.2?id=" + data.id );
    var resp = JSON.parse( result2 );
    console.log( "The value you asked for: " + resp.value );
}

main();

正如你所見(jiàn),async 函數(shù)可以想普通函數(shù)一樣被調(diào)用(如main()),而不需要包裝函數(shù)如runGenerator(..)或者ASQ().runner(..)的幫助。同時(shí),函數(shù)內(nèi)部不再使用yield,而是使用await(另外一個(gè)JavaScript關(guān)鍵字)關(guān)鍵字來(lái)告訴async 函數(shù)等待當(dāng)前promise得到返回值后繼續(xù)執(zhí)行。

基本上,async函數(shù)擁有通過(guò)一些包裝庫(kù)調(diào)用generator函數(shù)的大部分功能,同時(shí)關(guān)鍵是其被原生語(yǔ)法所支持。

是不是很酷???

同時(shí),像asynquence這樣的工具集使得我們能夠輕易的且充分利用generator函數(shù)完成異步工作。

總結(jié)

簡(jiǎn)單地說(shuō):通過(guò)把promise和generator函數(shù)兩個(gè)世界組合起來(lái)成為generator + yield promise(s)模式,該模式具有強(qiáng)大的能力及同步語(yǔ)法形式的異步表達(dá)能力。通過(guò)一些簡(jiǎn)單包裝的工具(很多庫(kù)已經(jīng)提供了這些工具),我們可以讓generator函數(shù)自動(dòng)執(zhí)行完成,并且提供了健全和同步語(yǔ)法形式的錯(cuò)誤處理機(jī)制。

同時(shí)在ES7+的將來(lái),我們也許將迎來(lái)async function函數(shù),async 函數(shù)將不需要上面那些工具庫(kù)就能夠解決上面遇到的那些問(wèn)題(至少對(duì)于基礎(chǔ)問(wèn)題是可行的)!

JavaScript的異步處理機(jī)制的未來(lái)是光明的,而且會(huì)越來(lái)越光明!我要帶墨鏡了。(譯者注:這兒是作者幽默的說(shuō)法)

但是,我們并沒(méi)有在這兒就結(jié)束本系列文章,這兒還有最后一個(gè)方面我們想要研究:

倘若你想要將兩個(gè)或多個(gè)generator函數(shù)結(jié)合在一起,讓他們獨(dú)立平行的運(yùn)行,并且在它們執(zhí)行的過(guò)程中來(lái)來(lái)回回得傳遞信息?這一定會(huì)成為一個(gè)相當(dāng)強(qiáng)大的特性,難道不是嗎?這一模式被稱(chēng)作“CSP”(communicating sequential processes)。我們將在下面一篇文章中解鎖CSP的能力。敬請(qǐng)密切關(guān)注。

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

轉(zhuǎn)載請(qǐng)注明本文地址:http://m.hztianpu.com/yun/87352.html

相關(guān)文章

  • ES6&ES7中的異步Generator函數(shù)異步編程

    摘要:傳統(tǒng)的異步方法回調(diào)函數(shù)事件監(jiān)聽(tīng)發(fā)布訂閱之前寫(xiě)過(guò)一篇關(guān)于的文章,里邊寫(xiě)過(guò)關(guān)于異步的一些概念。內(nèi)部函數(shù)就是的回調(diào)函數(shù),函數(shù)首先把函數(shù)的指針指向函數(shù)的下一步方法,如果沒(méi)有,就把函數(shù)傳給函數(shù)屬性,否則直接退出。 Generator函數(shù)與異步編程 因?yàn)閖s是單線程語(yǔ)言,所以需要異步編程的存在,要不效率太低會(huì)卡死。 傳統(tǒng)的異步方法 回調(diào)函數(shù) 事件監(jiān)聽(tīng) 發(fā)布/訂閱 Promise 之前寫(xiě)過(guò)一篇關(guān)...

    venmos 評(píng)論0 收藏0
  • 異步流程控制:7 行代碼學(xué)會(huì) co 模塊

    摘要:而在中是迭代器生成器,被創(chuàng)造性的拿來(lái)做異步流程控制了。當(dāng)執(zhí)行的時(shí)候,并不執(zhí)行函數(shù)體,而是返回一個(gè)迭代器。行代碼再看看文章開(kāi)頭的行代碼首先生成一個(gè)迭代器,然后執(zhí)行一遍,得到的是一個(gè)對(duì)象,里面再執(zhí)行。 廣告招人:阿里巴巴招前端,在這里你可以享受大公司的福利和技術(shù)體系,也有小團(tuán)隊(duì)的挑戰(zhàn)和成長(zhǎng)空間。聯(lián)系: qingguang.meiqg at alibaba-inc.com 首先請(qǐng)?jiān)徫业臉?biāo)題...

    tinna 評(píng)論0 收藏0
  • 談?wù)?em>ES6前后的異步編程

    摘要:回調(diào)函數(shù)這是異步編程最基本的方法。對(duì)象對(duì)象是工作組提出的一種規(guī)范,目的是為異步編程提供統(tǒng)一接口。誕生后,出現(xiàn)了函數(shù),它將異步編程帶入了一個(gè)全新的階段。 更多詳情點(diǎn)擊http://blog.zhangbing.club/Ja... Javascript 語(yǔ)言的執(zhí)行環(huán)境是單線程的,如果沒(méi)有異步編程,根本沒(méi)法用,非卡死不可。 為了解決這個(gè)問(wèn)題,Javascript語(yǔ)言將任務(wù)的執(zhí)行模式分成兩種...

    fizz 評(píng)論0 收藏0
  • ES6中的異步編程:Generators函數(shù)+Promise:最強(qiáng)大的異步處理方式

    摘要:更好的異步編程上面的方法可以適用于那些比較簡(jiǎn)單的異步工作流程。小結(jié)的組合目前是最強(qiáng)大,也是最優(yōu)雅的異步流程管理編程方式。 訪問(wèn)原文地址 generators主要作用就是提供了一種,單線程的,很像同步方法的編程風(fēng)格,方便你把異步實(shí)現(xiàn)的那些細(xì)節(jié)藏在別處。這讓我們可以用一種很自然的方式書(shū)寫(xiě)我們代碼中的流程和狀態(tài)邏輯,不再需要去遵循那些奇怪的異步編程風(fēng)格。 換句話說(shuō),通過(guò)將我們generato...

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

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

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

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

0條評(píng)論

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