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

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

繼續(xù)迭代器

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

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

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

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

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

快速滑稽劇

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

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

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

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

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

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

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

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

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

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

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

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

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

FOR 循環(huán)
警告!

立即進(jì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ù)生機(jī)。

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

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

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

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

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

他退出了,撅著嘴。

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

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

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

它的頭擱在其胸部,它眼睛里面的火熄滅了。它永遠(yuǎn)不會(huì)再移動(dòng)了。

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

她退出了舞臺。

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

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

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

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

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

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

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

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

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

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

我跳過了以上這些,主要是因?yàn)槿舨焕斫膺@些功能存在的原因,是很難去關(guān)心到他們的,更別說讓他們直直得留在你的腦袋里的。但是,隨著我們更多地思考我們的程序?qū)⑷绾问褂蒙善鳎覀儗⒖吹皆颉?/p>

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

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

doThings();

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

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

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

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

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

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

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

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

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

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

生成器負(fù)責(zé)機(jī)制

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

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

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

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

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

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

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

var results = yield getDataAndLatte(request.areaCode);

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

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

為了在上下文中說明,下面是上面顯示的整個(gè)會(huì)話的代碼:

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)得像一個(gè)行政助理一樣。

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

這個(gè)行政助理生成器運(yù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));
}

為了使事情順利進(jìn)行,我們需要?jiǎng)?chuàng)建一個(gè)生成器并運(yùn)行它,如下所示:

runGeneratorOnce(handle(request), undefined);

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

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

如何銷毀一個(gè)生成器

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

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

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

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

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

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

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

生成器一起工作

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

function* concat(iter1, iter2) {
  for (var value of iter1) {
    yield value;
  }
  for (var value of iter2) {
    yield value;
  }
}

ES6 為這個(gè)提供了一個(gè)簡寫方式:

function* concat(iter1, iter2) {
  yield* iter1;
  yield* iter2;
}

一個(gè)普通的 yield 表達(dá)式生成一個(gè)值;一個(gè) yield* 表達(dá)占用整個(gè)迭代器,并產(chǎn)生所有值。

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

function* factoredOutChunkOfCode() { ... }

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

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

退場

嗯,這是它的生成器!我希望你像我一樣非常的喜歡。回到生成器感覺非常好。

上一篇:未來前景