在线观看不卡亚洲电影_亚洲妓女99综合网_91青青青亚洲娱乐在线观看_日韩无码高清综合久久

鍍金池/ 教程/ HTML/ 繼續(xù)迭代器
生成器
箭頭函數(shù)
繼續(xù)迭代器
未來前景
簡介
非結(jié)構(gòu)化賦值
迭代器和 for-of 循環(huán)
代理
符號
模版字符串
let 和 const
容器
模塊

繼續(xù)迭代器

歡迎回到深度探索 ES6 的課程!我希望在暑假中,你也像我一樣,獲得盡可能多的樂趣。但是,一個程序員的生活不可能全是煙花和檸檬水?,F(xiàn)在是時候拿起我們放下的東西,同時,我已經(jīng)找到完美的話題來重新開始了。

生成器是 ES6 的一種新功能。我稱他們?yōu)?ES6 中最神奇的功能。我也談到了生成器可能是異步編程的未來。然后我寫了這段話:

關(guān)于生成器,還有很多需要談?wù)摰?.....但我認為這個博文的篇幅很長了,且現(xiàn)在夠撲朔迷離了。像發(fā)生器本身,我們應(yīng)該停下來,并采取了休息并另外找個時間繼續(xù)。

現(xiàn)在就是適合的時間。

你可以在博客中找到對應(yīng)文章的第一段就是上述的內(nèi)容。在閱讀這篇博文之前先復(fù)習(xí)一下前面的章節(jié)可能是最好的。繼續(xù),這是很有趣的。同時,這也是漫長且難懂的。但是,這是一只會說話的貓!

快速滑稽劇

上一次,我們專注于生成器的基本行為。這是可能是有些奇怪的,但是也不難理解。生成器函數(shù)是很大程度上像一個普通函數(shù)。其主要區(qū)別在于,一個生成器函數(shù)的主體部分不是一次全部運行的。它每次都運行一小部分,每次遇到 yield 語句時就會暫停執(zhí)行。

在上個關(guān)于生成器的博文的第 1 部分就已經(jīng)詳細解釋了。但我們從來沒有涉及到如何將所有的部分放在一起并工作的例子?,F(xiàn)在,讓我們一起來實現(xiàn)一下。

function* someWords() {
  yield "hello";
  yield "world";
}

for (var word of someWords()) {
  alert(word);
}

這段腳本足夠的直觀。但是如果你能夠觀察這里發(fā)生的一切的話,仿佛代碼中每一個比特位分別是一出戲中的不同人物一般,這將是一個相當(dāng)不同的腳本。它可能是出現(xiàn)在這樣的場景中:

場景 - 電腦內(nèi)部,白天

FOR 循環(huán)孤獨地站在舞臺上,戴著安全帽并背著一個剪貼板,這是所有的角色。

FOR 循環(huán)
( 呼喚 )
someWords( )!

生成器出現(xiàn)了:一個身材高大,黃銅地,上發(fā)條的紳士。它看起來足夠友好,但它仍然是一尊雕像。

FOR 循環(huán)
( 呼喚 ) ( 巧妙地拍著手 )
好吧!讓我們做一些東西。
( 針對生成器 )
.next( )!

生成器一下子就恢復(fù)地生機。

生成器
{值:“你好”,是否已完成:否}

以一個愚蠢地姿勢凍結(jié)住了。

FOR 循環(huán)
警告!

立即進入警告狀態(tài),睜大眼睛并且氣喘吁吁。我們感覺他總是這樣。

FOR 循環(huán)
告訴用戶“你好”。

警告轉(zhuǎn)身并立即下了舞臺。

ALERT
( 從舞臺上面下來并尖叫)
停止一切東西!
在 hacks.mozilla.org 的網(wǎng)頁說,
“你好”!

經(jīng)過幾秒鐘的停頓,警告重新回來了,在走向 FOR 和 LOOP 的過程中的所有的路口都停下來。

ALERT
用戶說一切都正常。
FOR 循環(huán)
( 巧妙地拍著手 )
好吧!讓我們做完一些東西。
( 轉(zhuǎn)過身回到生成器那邊 )
.next( )!

生成器又一次恢復(fù)生機。

生成器
{值:“世界”,是否已完成:否}

其用另一個愚蠢的姿勢凍結(jié)住了。

FOR 循環(huán)
警告!
ALERT
( 已經(jīng)在運行了 )
在上面了!
( 從舞臺上面下來,尖叫 )
停止一切東西!
在 hacks.mozilla.org 的網(wǎng)頁說,
“世界”!

同樣,一個暫停,然后 ALERT 吃力地跋涉回舞臺,突然垂頭喪氣。

ALERT
用戶再次說一切正常,但是...
但是請保護這個界面
免受創(chuàng)建其他對話的影響。

他退出了,撅著嘴。

FOR 循環(huán)
( 巧妙地拍著手 )
好吧!讓我們做完一些東西。
( 轉(zhuǎn)過身回到生成器那邊 )
.next( )!

生成器第三次恢復(fù)生機。

生成器
帶著尊嚴(yán)
{值:未定義,是否已完成:是}

它的頭擱在其胸部,它眼睛里面的火熄滅了。它永遠不會再移動了。

FOR 循環(huán)
是我午餐的時間了。

她退出了舞臺。

過了一會兒,垃圾收集器進入舞臺,撿起毫無生氣的生成器,然后離開了舞臺。

好吧,這不完全是哈姆雷特。但是你得到了一個直觀的感受。

正如你在舞臺劇里面看到的,當(dāng)一個生成器對象第一次出現(xiàn)的時候,他被暫停了。每次該對象的 .next( ) 方法被調(diào)用時,其蘇醒過來并運行一會兒。

動作是同步的且是單線程。需要注意的是,這些角色中只有一個可以在任何給定的時間實際做任何東西。這些角色從不打斷對方或與彼此談?wù)撨^。他們輪流發(fā)言,不管是誰在說話,只要他們想要的, 都可以繼續(xù)發(fā)言。( 就像莎士比亞!)

同時這個劇的一些版本每次展開,生成器就被送入一個 for 循環(huán)。在你的代碼中,.next( ) 方法的調(diào)用序列不會出現(xiàn)在任何地方。在這里,我把它全部放在了舞臺上,但對于你和你的程序,這一切將發(fā)生在幕后,因為生成器和 for-of 循環(huán)是被設(shè)計利用迭代器接口一起工作。

所以總結(jié)一下到目前為止的知識點:

  • 生成器對象是產(chǎn)生值的有禮貌的黃銅機器人。
  • 每個機器人的程序由一個單一的代碼塊組成:即創(chuàng)建它的生成器函數(shù)體。

如何關(guān)閉一個生成器

生成器有幾個我在第1部分沒有涵蓋的幾種繁瑣的附加功能:

  • generator.return( )
  • generator.next( ) 的可選參數(shù)
  • generator.throw(error)
  • yield*

我跳過了以上這些,主要是因為若不理解這些功能存在的原因,是很難去關(guān)心到他們的,更別說讓他們直直得留在你的腦袋里的。但是,隨著我們更多地思考我們的程序?qū)⑷绾问褂蒙善?,我們將看到原因?/p>

這里就是你可能已經(jīng)在一些情況下使用了的一種模式:

function doThings() {
  setup();
  try {
    // ... do some things ...
  } finally {
    cleanup();
  }
}

doThings();

清理可能涉及關(guān)閉連接或文件,釋放系統(tǒng)資源,或者只是更新 DOM 來關(guān)閉一個“正在進行中”自旋體。我們希望這種事情發(fā)生于判斷我們的工作是否成功完成,所以它會在一個 finally 塊中出現(xiàn)。

在一個發(fā)生器中,這將是什么樣子呢?

function* produceValues() {
  setup();
  try {
    // ... yield some values ...
  } finally {
    cleanup();
  }
}

for (var value of produceValues()) {
  work(value);
}

這看起來沒事。但是,這里有一個微妙的問題:調(diào)用 work(value) 不是在 try 語句塊里面。如果它拋出一個異常,對我們的清除步驟會有什么影響呢?

或者,假設(shè)這個 for 循環(huán)包含一個 break 或 return 語句。這對我們的清除步驟的影響又是什么呢?

有 ES6 做后盾,可以保證它肯定會執(zhí)行的。

當(dāng)我們第一次討論迭代器和 for 循環(huán)的時候,我們說迭代器接口包含一個可選 .return( ) 方法。當(dāng)?shù)髡f其已經(jīng)完成工作前迭代退出的時候,語言自動調(diào)用這個方法。生成器也支持此方法。調(diào)用 myGenerator.return( ) 會導(dǎo)致生成器運行任何 finally 塊,然后退出,就像當(dāng)前的 yield 點已被神秘地變成了一個 return 語句。

需要注意的是,在所有上下文中 .return( ) 不是由語言自動調(diào)用的,僅在語言使用迭代協(xié)議的情況,語言會自動調(diào)用。因此,它有可能為作為生成器的不需要運行 finally 塊的垃圾回收器。

這個特征如何發(fā)生功效呢?該生成器在一個需要進行一些設(shè)置任務(wù)中被凍結(jié),比如建設(shè)一個摩天大樓。突然有人拋出一個錯誤! for 循環(huán)將其捕獲并把它放在一邊。其 告訴生成器使用 .return( )。生成器平靜地拆除其所有的腳手架并關(guān)閉。然后 for 循環(huán)采取錯誤備份,然后繼續(xù)正常的異常處理。

生成器負責(zé)機制

到目前為止,我們已經(jīng)看到了在生成器和其用戶之間的對話已經(jīng)是相當(dāng)片面的了。以影院做比喻來打破:

http://wiki.jikexueyuan.com/project/es-six-deeply/images/generator-messages-small.jpg" alt="Alt text" />

用戶在負責(zé)中。生成器按需進行工作。但是這不是使用生成器進行編程的唯一方法。

在第一部分中,我說過生成器可以被用于異步編程。你現(xiàn)在做的異步回調(diào)或承諾鏈接都可以用生成器來替代。你可能想知道究竟是如何工作的。為什么產(chǎn)生( yield )的能力是足夠的(這畢竟是生成器的唯一的能力)?畢竟,異步代碼并不只是 yield。它可以產(chǎn)生某些東西,可以從文件和數(shù)據(jù)庫中的調(diào)用數(shù)據(jù),或者觸發(fā)對服務(wù)器的請求。然后返回到事件循環(huán)以等待那些異步進程完成。生成器究竟如何做到這一點的?如果沒有回調(diào),當(dāng)數(shù)據(jù)來的時候,生成器怎樣從這些文件,數(shù)據(jù)庫和服務(wù)器接收數(shù)據(jù)的?

為了開始尋找答案,想像一下,如果我們只有一種方法使得 .next( ) 調(diào)用者傳遞值回生成器,這會導(dǎo)致什么事情發(fā)生。只需這一個變化,我們可以有一段全新的對話:

http://wiki.jikexueyuan.com/project/es-six-deeply/images/generator-messages-2-small.jpg" alt="Alt text" />

同時,一個生成器的 .next( ) 方法實際情況下確實有一個可選擇的主題,且聰明的一點是,該說法隨后出現(xiàn)為生成器的 yield 表達式的返回值。這就意味著,yield 不是類似于 return 的表達式;它是一種新的表達式,一旦生成器恢復(fù),其就有一個值。

var results = yield getDataAndLatte(request.areaCode);

這單一行代碼做了很多的事情。

  • 它調(diào)用了 getDataAndLatte( )。可以這樣說,這個函數(shù)返回了一個字符串,即“get me the database records for area code...”這是我們在屏幕截圖上面看到的。
  • 其暫停了生成器,生成了一個字符串值。
  • 在這點上,任何長度的時間都可能過去。
  • 最終,某個人調(diào)用了 .next({data: ..., coffee: ...})。我們將該對象存儲在局部變量 results 中并從下一行代碼繼續(xù)運行。

為了在上下文中說明,下面是上面顯示的整個會話的代碼:

function* handle(request) {
  var results = yield getDataAndLatte(request.areaCode);
  results.coffee.drink();
  var target = mostUrgentRecord(results.data);
  yield updateStatus(target.id, "ready");
}

注意 yield 仍只是傳遞著其之前的意思:暫停發(fā)生器和將值返回給調(diào)用者。但是,變化有多大!該生成器希望從它的調(diào)用者那里獲取非常具體的支持。其似乎期望調(diào)用者可以表現(xiàn)得像一個行政助理一樣。

普通函數(shù)通常不是這樣的。他們的存在往往是為了滿足他們的調(diào)用者的需求。但生成器是你可以與之有一個談話的代碼,這使得生成器和他們的調(diào)用者之間可能有更廣泛的關(guān)系。

這個行政助理生成器運行者看起來是什么樣子的呢?它并不必須如此的復(fù)雜。它看起來可能是這樣的。

function runGeneratorOnce(g, result) {
  var status = g.next(result);
  if (status.done) {
    return;  // phew!
  }

  // The generator has asked us to fetch something and
  // call it back when we're done.
  doAsynchronousWorkIncludingEspressoMachineOperations(
    status.value,
    (error, nextResult) => runGeneratorOnce(g, nextResult));
}

為了使事情順利進行,我們需要創(chuàng)建一個生成器并運行它,如下所示:

runGeneratorOnce(handle(request), undefined);

在五月份,我提到過 Q.async( )。其作為一個庫的例子,其使用生成器作為異步的過程并且按需自動運行。runGeneratorOnce 就是這種東西。在實際使用中,生成器不會生成字符串說明他們需要調(diào)用者做的事情。他們很可能是返回一個 Promise 對象。

如果您已經(jīng)明白了 Promise,那么現(xiàn)在你應(yīng)該就明白生成器,你可能想嘗試修改 runGeneratorOnce 以支持 Pomise。這是一項困難的工作,但一旦你做到了,你就可以直接使用的 Pomise ,而不是現(xiàn)在 .then( ) 方法或者回調(diào)函數(shù)的方法去編寫復(fù)雜的異步算法了。

如何銷毀一個生成器

你有沒有注意到 runGeneratorOnce 是如何處理錯誤的?它忽略了他們!

嗯,這是不好的。我們真的想以某種方式將錯誤報告給生成器。同時,生成器也支持這一點:你可以調(diào)用 generator.throw(error),而不是 generator.next(result)。這將導(dǎo)致 yield 表達式拋出異常。像 .return( ) 一樣,生成器通常會被殺死,但如果目前的生成點是在 try 塊中,那么 catch 和 finally 塊是就可以處理這種異常,因此生成器可能從異常中恢復(fù)。

修改 runGeneratorOnce 以確保 .throw( ) 被適當(dāng)?shù)卣{(diào)用是另一個很好的練習(xí)。請記住,生成器里面拋出的異常始終傳播給調(diào)用者。所以 generator.throw(error) 將拋出錯誤給你,除非生成器自己捕獲了它!

當(dāng)生成器到達 yield 表達式和暫停的時候,其完成了一系列的可能性:

  • 有人可能會調(diào)用 generator.next(value)。在這種情況下,生成器在其離開的地方繼續(xù)執(zhí)行。
  • 有人可能會調(diào)用 generator.return(),可選地傳遞一個值。在這種情況下,不管它在做什么,生成器都不恢復(fù)。它值執(zhí)行 finally 語句塊。
  • 有人可能會調(diào)用 generator.throw(error)。該生成器表現(xiàn)得好像 yield 表達式調(diào)用拋出錯誤的函數(shù)。
  • 或者,也許沒有人會做以上任何的東西。該生成器可能永遠保持凍結(jié)。(是的,一個生成器進入一個 try 語句塊并且從來沒有執(zhí)行 finally 語句塊,這是可能的。在這個狀態(tài),一個生成器甚至可以被垃圾收集器回收。)

這沒有比一個普通的舊函數(shù)的調(diào)用復(fù)雜很多。只是 .return() 確實是一個新的可能性。

事實上,yield 與函數(shù)調(diào)用有很多共同點。當(dāng)你調(diào)用一個函數(shù),你暫時停了下來,對不對?您調(diào)用的函數(shù)在控制。它可能返回。它可能會拋出異常?;蛘?,它可能只是永遠循環(huán)下去。

生成器一起工作

讓我再多展示一個功能。假設(shè)我們寫了一個簡單的生成器函數(shù)來連接兩個可迭代的對象:

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* 表達占用整個迭代器,并產(chǎn)生所有值。

相同的語法也解決了另一個有趣的問題:如何從一個發(fā)生器中調(diào)用一個生成器的問題。在普通的函數(shù)中,我們可以從一個函數(shù)鏟起一堆代碼,重構(gòu)它使其變成一個單獨的函數(shù)且不改變其行為。很顯然,我們也要重構(gòu)生成器。但是,我們需要一種方法來調(diào)用分解出的子程序,并確保我們之前得到的每個值仍產(chǎn)生,即使它現(xiàn)在是一個產(chǎn)生這些值的子程序。yield* 是做到這一點的方法。

function* factoredOutChunkOfCode() { ... }

function* refactoredFunction() {
  ...
  yield* factoredOutChunkOfCode();
  ...
}

請想想,一個黃銅機器人委派子任務(wù)到另一個機器人。你可以看到這種想法對編寫大型生成器為基礎(chǔ)的項目,并保持代碼整潔有序的重要性了,就像函數(shù)是組織同步代碼的關(guān)鍵。

退場

嗯,這是它的生成器!我希望你像我一樣非常的喜歡?;氐缴善鞲杏X非常好。

上一篇:未來前景