深入理解Generator(譯文)

原文地址https://davidwalsh.name/es6-generators-dive
本文為generator系列文章的第二篇键闺。

如果你依然不熟悉ES6的generator懂昂,建議回去閱讀系列文章的第一篇肾筐。如果您已經(jīng)熟悉Generator基礎(chǔ)用法,接下來可以開始深入去了解一些細節(jié)部分掏颊。

錯誤處理

Generator函數(shù)中有一個非常棒的設(shè)計,就是在generator內(nèi)部代碼是同步的,無論外部是同步還是異步調(diào)用的看锉。

既然內(nèi)部代碼是同步的,那我們就可以在generator內(nèi)部使用try...catch去處理錯誤塔鳍。

function *foo() {
    try {
        var x = yield 3;
        console.log( "x: " + x ); // may never get here!
    }
    catch (err) {
        console.log( "Error: " + err );
    }
}

雖然這個函數(shù)會在yield 3這個語句停下來伯铣,并且停多久都可以,但是如果一個錯誤被傳入generator函數(shù)轮纫,try...catch語句還是會捕獲到這個錯誤懂傀。嘗試用普通的異步方式去處理,例如異步函數(shù)蜡感。

但是蹬蚁,怎么將一個錯誤傳入這個generator中呢?

var it = foo();
var res = it.next(); // { value:3, done:false}

// instead of resuming normally with another `next(..)` call,
// let's throw a wrench (an error) into the gears:
it.throw( "Oops!" ); // Error: Oops!

在上面的例子中郑兴,你可以看到我們使用了iterator的另外一個方法throw(..)犀斋,這個方法可以將將錯誤拋入generator函數(shù),就像這個錯誤是發(fā)生在generator函數(shù)在執(zhí)行yield語句停止的那個位置情连。此時try...catch就會想預(yù)期那樣的捕獲這個錯誤叽粹。

Note:如果你調(diào)用了throw(..)方法,但是沒有用try..catch去捕獲却舀,這個錯誤會像在普通函數(shù)里一樣被外部捕獲虫几,所以

function *foo() { }

var it = foo();
try {
    it.throw( "Oops!" );
}
catch (err) {
    console.log( "Error: " + err ); // Error: Oops!
}

內(nèi)部自身錯誤也是如此

function *foo() {
    var x = yield 3;
    var y = x.toUpperCase(); // could be a TypeError error!
    yield y;
}

var it = foo();

it.next(); // { value:3, done:false }

try {
    it.next( 42 ); // `42` won't have `toUpperCase()`
}
catch (err) {
    console.log( err ); // TypeError (from `toUpperCase()` call)
}

委派generator(Delegating Generators)

你也許會想要在generator函數(shù)內(nèi)部調(diào)用另外一個generator函數(shù)。我并不是指用常規(guī)方法實例化一個generator挽拔,而是將你的迭代控制權(quán)委托給另外一個generator函數(shù)辆脸,為了這樣做,我們使用yield語句的另外一種形式yield *
Example

function * foo() {
  yield 3;
  yield 4;
}

function *bar() {
  yield 1;
  yield 2;
  yield *foo(); // 'yield *' 將迭代控制權(quán)交給foo()
  yield 5;
}

for ( var v of bar()) {
  console.log(v)
}
// 1 2 3 4 5

讓我們來研究一下這是如何運行的螃诅。在for of循環(huán)中啡氢,yield 1yield 2如我們預(yù)期那樣直接輸出值状囱,但是當遇到yield *語句時,你會發(fā)現(xiàn)我們yield另外一個generator函數(shù)倘是,并且for..of循環(huán)中直接調(diào)用foo的迭代器亭枷,然后運行完畢后由取回bar的迭代器。

在上面例子中搀崭,我們使用了for of循環(huán)叨粘,實際上,即使是手動調(diào)用瘤睹,結(jié)果也和上面一樣宣鄙。

function *foo() {
    var z = yield 3;
    var w = yield 4;
    console.log( "z: " + z + ", w: " + w );
}

function *bar() {
    var x = yield 1;
    var y = yield 2;
    yield *foo(); // `yield*` delegates iteration control to `foo()`
    var v = yield 5;
    console.log( "x: " + x + ", y: " + y + ", v: " + v );
}

var it = bar();

it.next();      // { value:1, done:false }
it.next( "X" ); // { value:2, done:false }
it.next( "Y" ); // { value:3, done:false }
it.next( "Z" ); // { value:4, done:false }
it.next( "W" ); // { value:5, done:false }
// z: Z, w: W

it.next( "V" ); // { value:undefined, done:true }
// x: X, y: Y, v: V

雖然我們展示往下一級的委托,但是foo也可以繼續(xù)往下委托給別的generator默蚌。

另外一個比較詭異的地方就是yield *可以從被委托的generator函數(shù)獲取return的值(普通的yield語句的值為通過next方法傳入的)

function *foo() {
    yield 2;
    yield 3;
    return "foo"; // return value back to `yield*` expression
}

function *bar() {
    yield 1;
    var v = yield *foo();
    console.log( "v: " + v );
    yield 4;
}

var it = bar();

it.next(); // { value:1, done:false }
it.next(); // { value:2, done:false }
it.next(); // { value:3, done:false }
it.next(); // "v: foo"   { value:4, done:false }
it.next(); // { value:undefined, done:true }

就像你所看到的冻晤,yield *foo()將交出了迭代控制權(quán)直到交出去的迭代完成了,迭代完成時绸吸,yield *語句的將返回被委托的generator函數(shù)的返回值(此例中為foo函數(shù),返回值為foo字符串)

這是yieldyield *的顯著區(qū)別:yield語句的返回值是通過next()方法傳入的鼻弧,而yield *語句的返回值是被委托g(shù)enerator函數(shù)的返回值。

你也可以通過yield *語句從里外兩個方向處理錯誤锦茁,

function *foo() {
    try {
        yield 2;
    }
    catch (err) {
        console.log( "foo caught: " + err );
    }

    yield; // pause

    // now, throw another error
    throw "Oops!";
}

function *bar() {
    yield 1;
    try {
        yield *foo();
    }
    catch (err) {
        console.log( "bar caught: " + err );
    }
}

var it = bar();

it.next(); // { value:1, done:false }
it.next(); // { value:2, done:false }

it.throw( "Uh oh!" ); // will be caught inside `foo()`(譯者注:當調(diào)用了throw方法攘轩,err被catch后,發(fā)生err的語句之后的代碼都被執(zhí)行码俩,直到下一個yield停下度帮。)
// foo caught: Uh oh!

it.next(); // { value:undefined, done:true }  --> No error here!
// bar caught: Oops!

如上面代碼所示,throw('Uh oh!')語句在foo函數(shù)內(nèi)部拋出了一個錯誤并且被foo函數(shù)內(nèi)部的try...catch所捕獲稿存。類似的笨篷,在foo函數(shù)內(nèi)部的throw 'Oops!'所拋出的錯誤也被外部的try...catch所捕獲。如果錯誤內(nèi)外都沒有被捕捉瓣履,則會被再外層函數(shù)所捕捉率翅。

總結(jié)

Generator函數(shù)在內(nèi)部代碼有著同步執(zhí)行的特性,這表明你可以使用try...catch來捕捉其中的錯誤袖迎。generator的迭代器的throw方法可以將錯誤拋入generator上一次所停留的位置冕臭,所以也理所當然的可以被try...catch所捕獲。

yield*語句你將迭代權(quán)交給另一個generator燕锥,yield *語句的返回值就像個傳遞器辜贵,可以傳遞信息和錯誤。

但是归形,目前還有一個問題沒有回答托慨,generator函數(shù)式如何幫助我們實現(xiàn)異步的,我們目前所了解的都是同步的調(diào)用连霉。關(guān)鍵在于創(chuàng)建一個控制generator暫停和執(zhí)行的一個機制榴芳。我們在下一篇文章會進行講解。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末跺撼,一起剝皮案震驚了整個濱河市窟感,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌歉井,老刑警劉巖柿祈,帶你破解...
    沈念sama閱讀 211,639評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異哩至,居然都是意外死亡躏嚎,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,277評論 3 385
  • 文/潘曉璐 我一進店門菩貌,熙熙樓的掌柜王于貴愁眉苦臉地迎上來卢佣,“玉大人,你說我怎么就攤上這事箭阶⌒椴瑁” “怎么了?”我有些...
    開封第一講書人閱讀 157,221評論 0 348
  • 文/不壞的土叔 我叫張陵仇参,是天一觀的道長嘹叫。 經(jīng)常有香客問我,道長诈乒,這世上最難降的妖魔是什么罩扇? 我笑而不...
    開封第一講書人閱讀 56,474評論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮怕磨,結(jié)果婚禮上喂饥,老公的妹妹穿的比我還像新娘。我一直安慰自己肠鲫,他們只是感情好仰泻,可當我...
    茶點故事閱讀 65,570評論 6 386
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著滩届,像睡著了一般集侯。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上帜消,一...
    開封第一講書人閱讀 49,816評論 1 290
  • 那天棠枉,我揣著相機與錄音,去河邊找鬼泡挺。 笑死辈讶,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的娄猫。 我是一名探鬼主播贱除,決...
    沈念sama閱讀 38,957評論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼生闲,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了月幌?” 一聲冷哼從身側(cè)響起碍讯,我...
    開封第一講書人閱讀 37,718評論 0 266
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎扯躺,沒想到半個月后捉兴,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,176評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡录语,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,511評論 2 327
  • 正文 我和宋清朗相戀三年倍啥,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片澎埠。...
    茶點故事閱讀 38,646評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡虽缕,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出蒲稳,到底是詐尸還是另有隱情彼宠,我是刑警寧澤,帶...
    沈念sama閱讀 34,322評論 4 330
  • 正文 年R本政府宣布弟塞,位于F島的核電站凭峡,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏决记。R本人自食惡果不足惜摧冀,卻給世界環(huán)境...
    茶點故事閱讀 39,934評論 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望系宫。 院中可真熱鬧索昂,春花似錦、人聲如沸扩借。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,755評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽潮罪。三九已至康谆,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間嫉到,已是汗流浹背沃暗。 一陣腳步聲響...
    開封第一講書人閱讀 31,987評論 1 266
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留何恶,地道東北人孽锥。 一個月前我還...
    沈念sama閱讀 46,358評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親惜辑。 傳聞我的和親對象是個殘疾皇子唬涧,可洞房花燭夜當晚...
    茶點故事閱讀 43,514評論 2 348

推薦閱讀更多精彩內(nèi)容