<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                企業??AI智能體構建引擎,智能編排和調試,一鍵部署,支持知識庫和私有化部署方案 廣告
                > 原文出處:http://www.infoq.com/cn/articles/es6-in-depth-generators-continued 歡迎回到深入淺出ES6專欄,望你在ES6探索之旅中收獲知識與快樂!程序員們在工作之余應當補充些額外的知識,現在我們繼續深入淺出生成器,我已經為你們準備好非常棒的討論話題。 在之前的文章《[深入淺出ES6(三):生成器 Generators](http://www.infoq.com/cn/articles/es6-in-depth-generators)》中,我為大家介紹了ES6中引入的新特性——生成器(Generators),我認為它是ES6中最具魔力的特性,很可能是異步編程下一步的發展方向。后來我這樣寫道: > 生成器還有更多未提及的特性,例如:.throw()和.return()方法、可選參數.next()、yield*表達式語法。由于行文過長,估計觀眾老爺們已然疲乏,我們應該學習一下生成器,暫時yield在這里,剩下的干貨擇機為大家獻上。 此時此刻,我們再續前緣。 閱讀本文前,你最好先閱讀一下[文章的第1部分](http://www.infoq.com/cn/articles/es6-in-depth-generators),文章比較長,你可能看得云里霧里,但那兒有一只會說話的貓陪伴你,非常有趣! ## 快速回顧 在第三篇文章中,我們著重講解了生成器的基本行為。你可能對此感到陌生,但是并不難理解。生成器函數與普通函數有很多相似之處,它們之間最大的不同是,普通函數一次執行完畢,而生成器函數體每次執行一部分,每當執行到一個yield表達式的時候就會暫停。 盡管在[那篇文章](http://www.infoq.com/cn/articles/es6-in-depth-generators)中我們進行過詳細解釋,但我們始終未把所有特性結合起來給大家講解示例。現在就讓我們出發吧! ~~~ function* somewords() { yield "hello"; yield "world"; } for (var word of somewords()) { alert(word); } ~~~ 這段腳本簡單易懂,但是如果你把代碼中不同的比特位當做戲劇中的任務,你會發現它變得如此與眾不同。穿上新衣的代碼看起來是這樣的: * * * > (譯者注:下面這是原作者創作的一個劇本,他將ES6中的各種函數和語法擬人化,以講解生成器(Generator)的實現原理) 場景 - 另一個世界的計算機,白天 for loop女士獨自站在舞臺上,戴著一頂安全帽,手里拿著一個筆記板,上面記載著所有的事情。 ~~~ for loop: (電話響起) somewords()! ~~~ generator出現:這是一位高大的、有著一絲不茍紳士外表的黃銅機器人。 它看起來足夠友善,但給人的感覺仍然是冷冰冰的金屬。 ~~~ for loop: (瀟灑地拍了拍她的手) 好吧!我們去找些事兒做吧。 (對generator說) .next()! ~~~ generator動了起來,就像突然擁有了生命。 ~~~ generator: {value: "hello", done: false} ~~~ 然而猝不及防的,它以一個滑稽的姿勢停止了動作。 ~~~ for loop: alert! ~~~ alert小子飛快沖進舞臺,眼睛大睜,上氣不接下氣。我們感覺的到他一向如此。 ~~~ for loop: 對user說“hello”。 ~~~ alert小子轉身沖下舞臺。 ~~~ alert: (舞臺下,大聲尖叫) 一切都靜止了! 你正在訪問的頁面說, “hello”! ~~~ 停留了幾秒鐘后,alert小子跑回舞臺,穿過所有人滑停在for loop女士身邊。 ~~~ alert: user說ok。 for loop: (瀟灑地拍了拍她的手) 好吧!我們去找些事兒做吧。 (回到generator身邊) .next()! ~~~ generator又一次煥發生機。 ~~~ generator: {value: "world", done: false} ~~~ 它換了個姿勢又一次凍結。 ~~~ for loop: alert! alert: (已經跑起來) 正在搞定! (舞臺下,大聲尖叫) 一切都靜止了! 你正在訪問的頁面說, “world”! ~~~ 又一次暫停,然后alert突然跋涉回到舞臺,垂頭喪氣的。 ~~~ alert: user再一次說ok,但是… 但是請阻止這個頁面 創建額外的對話。 ~~~ 他噘著嘴離開了。 ~~~ for loop: (瀟灑地拍了拍她的手) 好吧!我們去找些事兒做吧。 (回到generator身邊) .next()! ~~~ generator第三次煥發生機。 ~~~ generator: (莊嚴的) {value: undefined, done: true} ~~~ 它的頭低下了,光芒從它的眼里消失。它不再移動。 ~~~ for loop 我的午餐時間到了。 ~~~ 她離開了。 一會兒,garbage collector(垃圾收集器)老頭進入,撿起了奄奄一息的generator,將它帶下舞臺。 好吧,這一出戲不太像哈姆雷特,但你應該可以想象得出來。 * * * 好吧,這一出戲不太像哈姆雷特,但你應該可以想象得出來。 正如你在戲劇中看到的,當生成器對象第一次出現時,它立即暫停了。每當調用它的`.next()`方法,它都會蘇醒并向前執行一部分。 所有動作都是單線程同步的。請注意,無論何時永遠只有一個真正活動的角色,角色們不會互相打斷,亦不會互相討論,他們輪流講話,只要他們的話沒有說完都可以繼續說下去。(就像莎士比亞一樣!) 每當`for-of`循環遍歷生成器時,這出戲的某個版本就展開了。這些`.next()`方法調用序列永遠不會在你的代碼的任何角落出現,在劇本里我把它們都放在舞臺上了,但是對于你和你的程序而言,所有這一切都應該在幕后完成,因為生成器和`for-of`循環就是被設計成通過[迭代器接口](http://www.ecma-international.org/ecma-262/6.0/index.html#sec-iterator-interface)聯結工作的。 所以,總結一下到目前為止所有的一切: * 生成器對象是可以產生值的優雅的黃銅機器人。 * 每個生成器函數體構成的單一代碼塊就是一個機器人。 ## 如何關停生成器 我在第1部分沒有提到這些繁瑣的生成器特性: * generator.return() * generator.next()的可選參數 * generator.throw(error) * yield* 如果你不理解這些特性存在得意義,就很難對它們提起興趣,更不用說理解它們的實現細節,所以我選擇直接跳過。但是當我們深入學習生成器時,勢必要仔細了解這些特性的方方面面。 你或許曾使用過這樣的模式: ~~~ function dothings() { setup(); try { // ... 做一些事情 } finally { cleanup(); } } dothings(); ~~~ 清理(cleanup)過程包括關閉連接或文件,釋放系統資源,或者只是更新dom來關閉“運行中”的加載動畫。我們希望無論任務成功完成與否都觸發清理操作,所以執行流入到`finally`代碼塊。 那么生成器中的清理操作看起來是什么樣的呢? ~~~ function* producevalues() { setup(); try { // ... 生成一些值 } finally { cleanup(); } } for (var value of producevalues()) { work(value); } ~~~ 這段代碼看起來很好,但是這里有一個問題:我們沒在`try`代碼塊中調用`work(value)`,如果它拋出異常,我們的清理步驟會如何執行呢? 或者假設`for-of`循環包含一條`break`語句或`return`語句。清理步驟又會如何執行呢? 放心,清理步驟無論如何都會執行,ES6已經為你做好了一切。 我們第一次討論[迭代器和for-of循環](http://www.infoq.com/cn/articles/es6-in-depth-iterators-and-the-for-of-loop)時曾說過,迭代器接口支持一個可選的`.return()`方法,每當迭代在迭代器返回`{done:true}`之前退出都會自動調用這個方法。生成器支持這個方法,`mygenerator.return()`會觸發生成器執行任一`finally`代碼塊然后退出,就好像當前的生成暫停點已經被秘密轉換為一條`return`語句一樣。 注意,`.return()`方法并不是在所有的上下文中都會被自動調用,只有當使用了迭代協議的情況下才會觸發該機制。所以也有可能生成器沒執行`finally`代碼塊就直接被垃圾回收了。 如何在舞臺上模擬這些特性?生成器被凍結在一個需要一些配置的任務(例如,建造一幢摩天大樓)中間。突然有人拋出一個錯誤!`for`循環捕捉到這個錯誤并將它放置在一遍,她告訴生成器執行`.return()`方法。生成器冷靜地拆除了所有腳手架并停工。然后`for`循環取回錯誤,繼續執行正常的異常處理過程。 ## 生成器主導模式 到目前為止,我們在劇本中看到的生成器(generator)和使用者(user)之間的對話非常有限,現在換一種方式繼續解釋: ![](https://box.kancloud.cn/2015-10-09_561757eeeea1b.png) 在這里使用者主導一切流程,生成器根據需要完成它的任務,但這不是使用生成器進行編程的唯一方式。 在第1部分中我曾經說過,生成器可以用來實現異步編程,完成你用異步回調或promise鏈所做的一切。我知道你一定想知道它是如何實現的,為什么yield的能力(這可是生成器專屬的特殊能力)足夠應對這些任務。畢竟,異步代碼不僅產生(yield)數據,還會觸發事件,比如從文件或數據庫中調用數據,向服務器發起請求并返回事件循環來等待異步過程結束。生成器如何實現這一切?它又是如何不借助回調力量從文件、數據庫或服務器中接受數據? 為了開始找出答案,考慮一下如果`.next()`的調用者只有一種方法可以傳值返回給生成器會發生什么?僅僅是這一點改變,我們就可能創造一種全新的會話形式: ![](https://box.kancloud.cn/2015-10-09_561757ef15c84.png) 事實上,生成器的`.next()`方法接受一個可選參數,參數稍后會作為`yield`表達式的返回值出現在生成器中。那就是說,`yield`語句與`return`語句不同,它是一個只有當生成器恢復時才會有值的表達式。 ~~~ var results = yield getdataandlatte(request.areacode); ~~~ 這一行代碼完成了許多功能: * 調用`getdataandlatte()`,假設函數返回我們在截圖中看到的字符串“`get me the database records for area code...`”。 * 暫停生成器,生成字符串值。 * 此時可以暫停任意長的時間。 * 最終,直到有人調用`.next({data: ..., coffee: ...})`,我們將這個對象存儲在本地變量`results`中并繼續執行下一行代碼。 下面這段代碼完整地展示了這一行代碼完整的上下文會話: ~~~ function* handle(request) { var results = yield getdataandlatte(request.areacode); results.coffee.drink(); var target = mosturgentrecord(results.data); yield updatestatus(target.id, "ready"); } ~~~ `yield`仍然保持著它的原始含義:暫停生成器,返回值給調用者。但是確實也發生了變化!這里的生成器期待來自調用者的非常具體的支持行為,就好像調用者是它的行政助理一樣。 普通函數則與之不同,通常更傾向于滿足調用者的需求。但是你可以借助生成器創造一段對話,拓展生成器與其調用者之間可能存在的關系。 這個行政助理生成器運行器可能是什么樣的?它大可不必很復雜,就像這樣: ~~~ function rungeneratoronce(g, result) { var status = g.next(result); if (status.done) { return; // phew! } // 生成器請我們去獲取一些東西并且 // 當我們搞定的時候再回調它 doasynchronousworkincludingespressomachineoperations( status.value, (error, nextresult) => rungeneratoronce(g, nextresult)); } ~~~ 為了讓這段代碼運行起來,我們必須創建一個生成器并且運行一次,像這樣: ~~~ rungeneratoronce(handle(request), undefined); ~~~ 在之前的文章中,我一個庫的示例中提到`Q.async()`,在那個庫中,生成器是可以根據需要自動運行的異步過程。`rungeneratoronce`正式這樣的一個具體實現。事實上,生成器一般會生成Promise對象來告訴調用者要做的事情,而不是生成字符串來大聲告訴他們。 如果你已經理解了Promise的概念,現在又理解了生成器的概念,你可以嘗試修改`rungeneratoronce`的代碼來支持Promise。這個任務不簡單,但是一旦成功,你將能夠用Promise線性書寫復雜的異步算法,而不僅僅通過`.then()`方法或回調函數來實現異步功能。 # 如何銷毀生成器 你是否有看到`rungeneratoronce`的錯誤處理過程?答案一定是沒有,因為上面的示例中直接忽略了錯誤! 是的,那樣做不好,但是如果我們想要以某種方法給生成器報告錯誤,可以嘗試一下這個方法:當有錯誤產生時,不要繼續調用`generator.next(result)`方法,而應該調用`generator.throw(error)`方法來拋出`yield`表達式,進而像`.return()`方法一樣終止生成器的執行。但是如果當前的生成暫停點在一個`try`代碼塊中,那么會`catch`到錯誤并執行`finally`代碼塊,生成器就恢復執行了。 另一項艱巨的任務來啦,你需要修改`rungeneratoronce`來確保`.throw()`方法能夠被恰當地調用。請記住,生成器內部拋出的異常總是會傳播到調用者。所以無論生成器是否捕獲錯誤,`generator.throw(error)`都會拋出`error`并立即返回給你。 當生成器執行到一個`yield`表達式并暫停后可以實現以下功能: * 調用`generator.next(value)`,生成器從離開的地方恢復執行。 * 調用`generator.return()`,傳遞一個可選值,生成器只執行`finally`代碼塊并不再恢復執行。 * 調用`generator.throw(error)`,生成器表現得像是`yield`表達式調用一個函數并拋出錯誤。 * 或者,什么也不做,生成器永遠保持凍結狀態。(是的,對于一個生成器來說,很可能執行到一個`try`代碼塊,永不執行`finally`代碼塊。這種狀態下的生成器可以被垃圾收集器回收。) 看起來生成器函數與普通函數的復雜度相當,只有`.return()`方法顯得不太一樣。 事實上,`yield`與函數調用有許多共通的地方。當你調用一個函數,你就暫時停止了,對不對?你調用的函數取得主導權,它可能返回值,可能拋出錯誤,或者永遠循環下去。 # 結合生成器實現更多功能 我再展示一個特性。假設我們寫一個簡單的生成器函數聯結兩個可迭代對象: ~~~ function* concat(iter1, iter2) { for (var value of iter1) { yield value; } for (var value of iter2) { yield value; } } ~~~ es6支持這樣的簡寫方式: ~~~ function* concat(iter1, iter2) { yield* iter1; yield* iter2; } ~~~ 普通`yield`表達式只生成一個值,而`yield*`表達式可以通過迭代器進行迭代生成所有的值。 這個語法也可以用來解決另一個有趣的問題:在生成器中調用生成器。在普通函數中,我們可以從將一個函數重構為另一個函數并保留所有行為。很顯然我們也想重構生成器,但我們需要一種調用提取出來的子例程的方法,我們還需要確保,子例程能夠生成之前生成的每一個值。`yield*`可以幫助我們實現這一目標。 ~~~ function* factoredoutchunkofcode() { ... } function* refactoredfunction() { ... yield* factoredoutchunkofcode(); ... } ~~~ 考慮一下這樣一個場景:一個黃銅機器人將子任務委托給另一個機器人,函數對組織同步代碼來說至關重要,所以這種思想可以使基于生成器特性的大型項目保持簡潔有序。 ## 退場 好啦,這就是有關生成器的全部內容!我希望你如我般享受,很高興回來。 下一篇文章,我們會討論另一種令人興奮的特性,它是在es6中加入的一種全新的對象,它很微妙,又很復雜,你可能到最后都不知道自己一直在使用這一特性。下一次請記得回來,我們一起深入淺出es6代理(proxy)。
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看