一文帶你搞懂JavaScript的Generator函數

點擊上方“ 前端進階學習交流 ”,進行關注

回覆“前端”即可獲贈前端相關學習資料

輕生本爲國,重氣不關私。

大家好,我是進階學習者。

一、概念

常規函數只會返回一個單一值(或者不返回任何值)。

而 Generator 可以按需一個接一個地返回(“yield”)多個值。它們可與 iterable 完美配合使用,從而可以輕鬆地創建數據流。


二、Generator 函數

要創建一個 generator,需要一個特殊的語法結構:function*,即所謂的 “generator function”。

Generator 函數與常規函數的行爲不同。在此類函數被調用時,它不會運行其代碼。而是返回一個被稱爲 “generator object” 的特殊對象,來管理執行流程。

例如,可以創建一個 generator 並獲取其第一個產出的(yielded)值:

function* generateSequence() {  yield 1;  yield 2;  return 3;}let generator = generateSequence();let one = generator.next();alert(JSON.stringify(one)); // {value: 1, done: false}

截至目前,只獲得了第一個值,現在函數執行處在第二行:

讓再次調用 generator.next()。代碼恢復執行並返回下一個 yield 的值:

let two = generator.next();
alert(JSON.stringify(two)); // {value: 2, done: false}

如果第三次調用 generator.next(),代碼將會執行到 return 語句,此時就完成這個函數的執行:

let three = generator.next();alert(JSON.stringify(three)); // {value: 3, done: true}

運行結果:


三、Generator 是可迭代的

當你看到 next() 方法,或許你已經猜到了 generator 是 可迭代(iterable)的。(譯註:next() 是 iterator 的必要方法)

可以使用 for..of 循環遍歷它所有的值:

function* generateSequence() {  yield 1;  yield 2;  return 3;}let generator = generateSequence();for(let value of generator) {  alert(value); // 1,然後是 2}

運行結果:

for..of 寫法是不是看起來比 .next().value 優雅多了?

注:

上面這個例子會先顯示 1,然後是 2,然後就沒了。它不會顯示 3!

這是因爲當 done: true 時,for..of 循環會忽略最後一個 value。因此,如果想要通過 for..of 循環顯示所有的結果,必須使用 yield 返回它們:

function* generateSequence() {  yield 1;  yield 2;  yield 3;}let generator = generateSequence();for(let value of generator) {  alert(value); // 1,然後是 2,然後是 3}

因爲 generator 是可迭代的,可以使用 iterator 的所有相關功能。

例如:spread 語法 ...:

function* generateSequence() {  yield 1;  yield 2;  yield 3;}let sequence = [0, ...generateSequence()];alert(sequence); // 0, 1, 2, 3

運行結果:


四、Generator 組合

Generator 組合(composition)是 generator 的一個特殊功能,它允許透明地(transparently)將 generator 彼此“嵌入(embed)”到一起。

例如,有一個生成數字序列的函數:組合的 generator 的例子:

function* generateSequence(start, end) {  for (let i = start; i <= end; i++) yield i;}function* generatePasswordCodes() {  // 0..9  yield* generateSequence(48, 57);  // A..Z  yield* generateSequence(65, 90);  // a..z  yield* generateSequence(97, 122);}let str = '';for(let code of generatePasswordCodes()) {  str += String.fromCharCode(code);}alert(str); // 0..9A..Za..z

運行結果:

generator.throw

上面的例子中觀察到的那樣,外部代碼可能會將一個值傳遞到 generator,作爲 yield 的結果。

但是它也可以在那裏發起(拋出)一個 error。這很自然。

因爲 error 本身也是一種結果,要向 yield 傳遞一個 error,應該調用 generator.throw(err)。

在這種情況下,err 將被拋到對應的 yield 所在的那一行。

例:

"2 + 2?" 的 yield 導致了一個 error:

function* gen() {  try {    let result = yield "2 + 2 = ?"; // (1)    alert("The execution does not reach here, because the exception is thrown above");  } catch(e) {    alert(e); // 顯示這個 error  }}let generator = gen();
let question = generator.next().value;
generator.throw(new Error("The answer is not found in my database")); // (2)

運行結果:


五、總結

本文基於JavaScript基礎,介紹了Generator函數 ,重點介紹瞭如何進行Generator 組合,採用圖文結合的方式。採用JavaScript語言,能夠更直觀的的理解,希望能夠幫助讀者更好的學習。

歡迎大家積極嘗試,有時候看到別人實現起來很簡單,但是到自己動手實現的時候,總會有各種各樣的問題,切勿眼高手低,勤動手,纔可以理解的更加深刻。

代碼很簡單,希望對你學習有幫助。

------------------- End -------------------

往期精彩文章推薦:

歡迎大家點贊,留言,轉發,轉載,感謝大家的相伴與支持

想加入前端學習羣請在後臺回覆【入羣

萬水千山總是情,點個【在看】行不行

本文分享自微信公衆號 - 前端進階學習交流(gh_cf4e462f0835)。
如有侵權,請聯繫 [email protected] 刪除。
本文參與“OSC源創計劃”,歡迎正在閱讀的你也加入,一起分享。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章