摘要:同時(shí),迭代器有一個(gè)方法來向函數(shù)中暫停處拋出一個(gè)錯(cuò)誤,該錯(cuò)誤依然可以通過函數(shù)內(nèi)部的模塊進(jìn)行捕獲處理。
ES6 Generators:完整系列本文翻譯自:Diving Deeper With ES6 Generators
由于個(gè)人能力有限,翻譯中難免有紕漏和錯(cuò)誤,望不吝指正issue
The Basics Of ES6 Generators
Diving Deeper With ES6 Generators
Going Async With ES6 Generators
Getting Concurrent With ES6 Generators
如果你依然對(duì)ES6 generators不是很熟悉,建議你閱讀本系列第一篇文章“第一部分:ES6 Generators基礎(chǔ)指南”,并練習(xí)其中的代碼片段。一旦你覺得對(duì)基礎(chǔ)部分掌握透徹了,那我們就可以開始深入理解Generator函數(shù)的一些細(xì)節(jié)部分。
錯(cuò)誤處理ES6 generators設(shè)計(jì)中最為強(qiáng)大部分莫過于從語義上理解generator中的代碼都是同步的,盡管外部的迭代控制器是異步執(zhí)行的。
也就是說,你可以使用簡(jiǎn)單的錯(cuò)誤處理技術(shù)來對(duì)generators函數(shù)進(jìn)行容錯(cuò)處理, 也就是你最為熟悉的try...catch機(jī)制。
例如:
function *foo() { try { var x = yield 3; console.log( "x: " + x ); // may never get here! } catch (err) { console.log( "Error: " + err ); } }
盡管上面例子中的foo generator函數(shù)會(huì)在yield 3表達(dá)式后暫停執(zhí)行,并且可能暫停任意長(zhǎng)的時(shí)間,如果向generator函數(shù)內(nèi)部傳入一個(gè)錯(cuò)誤,generator函數(shù)內(nèi)部的try...catch模塊將會(huì)捕獲傳入的錯(cuò)誤!就像通過回調(diào)函數(shù)等常見的異步處理機(jī)制一樣來處理錯(cuò)誤。:)
但是,錯(cuò)誤究竟是怎樣傳遞到generator函數(shù)內(nèi)部的呢?
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!
如上代碼,你會(huì)看到iterator的另外一個(gè)方法- -throw(..)- -,該方法向generator函數(shù)內(nèi)部傳入一個(gè)錯(cuò)誤,該錯(cuò)誤就如同在generator函數(shù)內(nèi)部暫停執(zhí)行的yield語句處拋出的錯(cuò)誤一樣,正如你所愿,try...catch模塊捕獲了通過throw方法拋出的錯(cuò)誤。
注意:如果你通過throw(..)方法向generator函數(shù)內(nèi)部拋出一個(gè)錯(cuò)誤,同時(shí)在函數(shù)內(nèi)部又沒有try...catch模塊來捕獲錯(cuò)誤,該錯(cuò)誤(如同正常的錯(cuò)誤冒泡機(jī)制)將從generator函數(shù)冒泡到函數(shù)外部(如果始終都沒對(duì)該錯(cuò)誤進(jìn)行處理,該錯(cuò)誤將冒泡到最外層成為未捕獲錯(cuò)誤)。代碼如下:
function *foo() { } var it = foo(); try { it.throw( "Oops!" ); } catch (err) { console.log( "Error: " + err ); // Error: Oops! }
顯而易見,反向的錯(cuò)誤處理依然能夠正常工作(譯者注:generator函數(shù)內(nèi)部拋出錯(cuò)誤,在generator外部捕獲):
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) }代理 Generators函數(shù)
在使用generator函數(shù)的過程中,另外一件你可能想要做的事就是在generator函數(shù)內(nèi)部調(diào)用另外一個(gè)generator函數(shù)。這兒我并不是指在普通函數(shù)內(nèi)部執(zhí)行g(shù)enerator函數(shù),實(shí)際上是把迭代控制權(quán)委托給另外一個(gè)generator函數(shù)。為了完成這件工作,我們使用了yield關(guān)鍵字的變種:yield *(“yield star”)。
例如:
function *foo() { yield 3; yield 4; } function *bar() { yield 1; yield 2; yield *foo(); // `yield *` delegates iteration control to `foo()` yield 5; } for (var v of bar()) { console.log( v ); } // 1 2 3 4 5
在第一篇文章中已經(jīng)提及(在第一篇文章中,我使用function *foo() { }的語法格式,而不是function* foo() { }),在這里,我們依然使用yield *foo(),而不是yield* foo(),盡管很多文章/文檔喜歡采用后面一種語法格式。我認(rèn)為前面一種語法格式更加準(zhǔn)確/清晰得表達(dá)此語法含義。
讓我們來分解上面代碼是如何工作的。yield 1和yield 2表達(dá)式直接將值通過for..of循環(huán)(隱式)調(diào)用next()傳遞到外部,正如我們已經(jīng)理解并期待的那樣。
在代碼執(zhí)行過程中,我們遇到了yield *表達(dá)式,你將看到我們通過執(zhí)行foo()將控制權(quán)交給了另外一個(gè)generator函數(shù)。因此我們基本上就是出產(chǎn)/委托給了另外一個(gè)generator函數(shù)的迭代器- -也許這就是最準(zhǔn)確的理解代理generator函數(shù)如何工作的。
一旦yield *表達(dá)式(臨時(shí)的)在*bar()函數(shù)中將控制權(quán)委托給*foo()函數(shù),那么現(xiàn)在for..of循環(huán)中的next()方法的執(zhí)行將完全控制foo(),因此yield 3和yield 4表達(dá)式將他們的值通過for..of循環(huán)返回到外部。
當(dāng)*foo()運(yùn)行結(jié)束,控制權(quán)重新交回最初的generator函數(shù),最后在外層bar函數(shù)中執(zhí)行yield 5。
簡(jiǎn)單起見,在上面的實(shí)例中,我們僅通過yield表達(dá)式將值傳遞到generator函數(shù)外部,當(dāng)然,如果我們不用for..of循環(huán),而是手動(dòng)的執(zhí)行迭代器的next()方法來向函數(shù)內(nèi)部傳遞值,這些值也會(huì)按你所期待的方式傳遞給通過yield *代理的generator函數(shù)中:
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
盡管上面的代碼中我們只展示了嵌套一層的代理generator函數(shù),但是沒有理由*foo()不可以通過yield *表達(dá)式繼續(xù)代理其他的generator迭代器,甚至繼續(xù)嵌套代理其他generator函數(shù),等等。
yield *表達(dá)式可以實(shí)現(xiàn)另外一個(gè)竅門,就是yield *表達(dá)式將會(huì)返回被代理generator函數(shù)的函數(shù)返回值。
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)(調(diào)用next()方法)至到其運(yùn)行完成,當(dāng)前執(zhí)行完成,foo()函數(shù)的函數(shù)return值(本例中是"foo"字符串)將會(huì)作為yield *表達(dá)式的值,在上例中將該值賦值給變量v。
這是一個(gè)yield和yield*表達(dá)式有趣的區(qū)別:在yield表達(dá)式中,表達(dá)式的返回值是通過隨后的next()方法調(diào)用傳遞進(jìn)來的,但是在yield *表達(dá)式中,它將獲取到被代理generator函數(shù)的return值(因?yàn)?b>next()方法顯式的將值傳遞到被代理的generator函數(shù)中)。
你依然可以雙向的對(duì)yield *代理進(jìn)行錯(cuò)誤處理(如上所述):
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()` // foo caught: Uh oh! it.next(); // { value:undefined, done:true } --> No error here! // bar caught: Oops!
如你所見,throw("Uh oh!")通過yield*代理將錯(cuò)誤拋出,然后*foo()函數(shù)內(nèi)部的try..catch模塊捕獲到錯(cuò)誤。同樣地,在*foo()函數(shù)內(nèi)部通過throw "Oops!"拋出錯(cuò)誤冒泡到*bar()函數(shù)中被另外一個(gè)try..catch模塊捕獲,如果我們沒有捕獲到其中的某一條錯(cuò)誤,該錯(cuò)誤將會(huì)按你所期待的方式繼續(xù)向上冒泡。
總結(jié)Generators函數(shù)擁有同步執(zhí)行的語義,這也意味著你可以通過try..catch錯(cuò)誤處理機(jī)制來橫跨yield語句進(jìn)行錯(cuò)誤處理。同時(shí),generator迭代器有一個(gè)throw()方法來向generator函數(shù)中暫停處拋出一個(gè)錯(cuò)誤,該錯(cuò)誤依然可以通過generator函數(shù)內(nèi)部的try..catch模塊進(jìn)行捕獲處理。
yield *關(guān)鍵字允許你將迭代控制權(quán)從當(dāng)前generator函數(shù)委托給其他generator函數(shù)。結(jié)果就是,yield *將扮演一個(gè)雙向的信息和錯(cuò)誤傳遞角色。
但是到目前為止,一個(gè)基礎(chǔ)的問題依然沒有解決:generator函數(shù)怎么幫助我們處理異步模式?在以上兩篇文章中我們一直討論generator函數(shù)的同步迭代模式。
構(gòu)想generator函數(shù)異步機(jī)制的關(guān)鍵點(diǎn)在于,通過generator函數(shù)的暫停執(zhí)行來開始一個(gè)異步任務(wù),然后通過generator函數(shù)的重新啟動(dòng)(通過迭代器的next()方法的執(zhí)行)來結(jié)束上面的異步任務(wù)。我們可以在接下來的文章中發(fā)現(xiàn)generator函數(shù)形式各樣的異步控制機(jī)制。近期期待!
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://specialneedsforspecialkids.com/yun/84715.html
摘要:如果你已經(jīng)理解基礎(chǔ)可以直接跳過這部分和語法部分,直接看深入理解的部分。的參數(shù)可以傳入一個(gè)參數(shù),來作為上一次的表達(dá)式的返回值,就像我們上面說的會(huì)讓等于。 這篇文章旨在幫你真正了解Generator,文章較長(zhǎng),不過如果能花時(shí)間耐心看完,相信你已經(jīng)能夠完全理解generator 為什么要用generator 在前端開發(fā)過程中我們經(jīng)常需要先請(qǐng)求后端的數(shù)據(jù),再用拿來的數(shù)據(jù)進(jìn)行使用網(wǎng)頁頁面渲染等操...
摘要:形式非必須,也非必須調(diào)用把用函數(shù)表示在調(diào)用的時(shí)候用函數(shù)代碼更加同步化三是什么異步操作的終極解決方案寫法四總結(jié)不管用還是用還是用,都保證你寫的的返回值是一個(gè)對(duì)象 一、promise入門 1. Promise對(duì)象是什么 回調(diào)函數(shù)的另一種原生實(shí)現(xiàn),比之前回調(diào)函數(shù)的寫法機(jī)構(gòu)清晰,功能強(qiáng)大, 2.以前回調(diào)這么寫 function a(fn){ let h = 1; setTime...
摘要:的翻譯文檔由的維護(hù)很多人說,阮老師已經(jīng)有一本關(guān)于的書了入門,覺得看看這本書就足夠了。前端的異步解決方案之和異步編程模式在前端開發(fā)過程中,顯得越來越重要。為了讓編程更美好,我們就需要引入來降低異步編程的復(fù)雜性。 JavaScript Promise 迷你書(中文版) 超詳細(xì)介紹promise的gitbook,看完再不會(huì)promise...... 本書的目的是以目前還在制定中的ECMASc...
摘要:缺點(diǎn)無法取消當(dāng)處于狀態(tài)時(shí),無法得知目前進(jìn)展到哪一個(gè)階段錯(cuò)誤不能被生成器什么是函數(shù)是提供的一種異步編程解決方案,語法行為與傳統(tǒng)函數(shù)完全不同函數(shù)有多種理解角度。 JavaScript的執(zhí)行機(jī)制在上篇文章中進(jìn)行了深入的探討,那么既然是一門單線程語言,如何進(jìn)行良好體驗(yàn)的異步編程呢 回調(diào)函數(shù)Callbacks 當(dāng)程序跑起來時(shí),一般情況下,應(yīng)用程序(application program)會(huì)時(shí)常通...
摘要:缺點(diǎn)無法取消當(dāng)處于狀態(tài)時(shí),無法得知目前進(jìn)展到哪一個(gè)階段錯(cuò)誤不能被生成器什么是函數(shù)是提供的一種異步編程解決方案,語法行為與傳統(tǒng)函數(shù)完全不同函數(shù)有多種理解角度。 JavaScript的執(zhí)行機(jī)制在上篇文章中進(jìn)行了深入的探討,那么既然是一門單線程語言,如何進(jìn)行良好體驗(yàn)的異步編程呢 回調(diào)函數(shù)Callbacks 當(dāng)程序跑起來時(shí),一般情況下,應(yīng)用程序(application program)會(huì)時(shí)常通...
閱讀 2739·2023-04-25 14:21
閱讀 1174·2021-11-23 09:51
閱讀 4013·2021-09-22 15:43
閱讀 610·2019-08-30 15:55
閱讀 1559·2019-08-29 11:28
閱讀 2445·2019-08-26 11:44
閱讀 1682·2019-08-23 18:15
閱讀 2880·2019-08-23 16:42