摘要:不參與迭代迭代會(huì)執(zhí)行所有的,也就是說(shuō),在迭代后的對(duì)象將不會(huì)再返回任何有效的值我們可以在迭代器對(duì)象上直接調(diào)用,來(lái)終止后續(xù)的代碼執(zhí)行。
前兩年大量的在寫Generator+co,用它來(lái)寫一些類似同步的代碼
但實(shí)際上,Generator并不是被造出來(lái)干這個(gè)使的,不然也就不會(huì)有后來(lái)的async、await了
Generator是一個(gè)可以被暫停的函數(shù),并且何時(shí)恢復(fù),由調(diào)用方?jīng)Q定
希望本文可以幫助你理解Generator究竟是什么,以及怎么用
放一張圖來(lái)表示我對(duì)Generator的理解:
一個(gè)咖啡機(jī),雖說(shuō)我并不喝咖啡,可惜找不到造王老吉的機(jī)器-.-
我所理解的Generator咖啡機(jī)大概就是這么的一個(gè)樣子的:
首先,我們往機(jī)器里邊放一些咖啡豆
等我們想喝咖啡的時(shí)候,就可以按開關(guān)(gen.next()),機(jī)器開始磨咖啡豆、煮咖啡、接下來(lái)就得到咖啡了
等接滿了一杯咖啡后,閥門就會(huì)自動(dòng)關(guān)閉(yield)
如果你一開始往機(jī)器里邊放的咖啡豆很多的話,此時(shí),機(jī)器里邊還是會(huì)有一些剩余的,下次再想喝還可以繼續(xù)按開關(guān),執(zhí)行(磨豆、煮咖啡、接咖啡)這一套操作
拿Generator將上述咖啡機(jī)實(shí)現(xiàn)一下:
function * coffeeMachineGenerator (beans) { do { yield cookCoffee() } while (--beans) // 煮咖啡 function cookCoffee () { console.log("cooking") return "Here you are" } } // 往咖啡機(jī)放咖啡豆 let coffeeMachine = coffeeMachineGenerator(10) // 我想喝咖啡了 coffeeMachine.next() // 我在3秒后還會(huì)喝咖啡 setTimeout(() => { coffeeMachine.next() }, 3 * 1e3)
代碼運(yùn)行后,我們首先會(huì)得到一條cooking的log,
然后在3s后會(huì)再次得到一條log。
這就解釋了Generator是什么:
一個(gè)可以暫停的迭代器
調(diào)用next來(lái)獲取數(shù)據(jù)(我們自己來(lái)決定是否何時(shí)煮咖啡)
在遇到yield以后函數(shù)的執(zhí)行就會(huì)停止(接滿了一杯,閥門關(guān)閉)
我們來(lái)決定何時(shí)運(yùn)行剩余的代碼next(什么時(shí)候想喝了再去煮)
這是Generator中最重要的特性,我們只有在真正需要的時(shí)候才獲取下一個(gè)值,而不是一次性獲取所有的值
Generator的語(yǔ)法聲明Generator函數(shù)有很多種途徑,最重要的一點(diǎn)就是,在function關(guān)鍵字后添加一個(gè)*
function * generator () {} function* generator () {} function *generator () {} let generator = function * () {} let generator = function* () {} let generator = function *() {} // 錯(cuò)誤的示例 let generator = *() => {} let generator = ()* => {} let generator = (*) => {}
或者,因?yàn)槭且粋€(gè)函數(shù),也可以作為一個(gè)對(duì)象的屬性來(lái)存在:
class MyClass { * generator() {} *generator2() {} } const obj = { *generator() {} * generator() {} }generator的初始化與復(fù)用
一個(gè)Generator函數(shù)通過(guò)調(diào)用兩次方法,將會(huì)生成兩個(gè)完全獨(dú)立的狀態(tài)機(jī)
所以,保存當(dāng)前的Generator對(duì)象很重要:
function * generator (name = "unknown") { yield `Your name: ${name}` } const gen1 = generator() const gen2 = generator("Niko Bellic") gen1.next() // { value: Your name: unknown , done: false} gen2.next() // { value: Your name: Niko Bellic, done: false}Method: next()
最常用的next()方法,無(wú)論何時(shí)調(diào)用它,都會(huì)得到下一次輸出的返回對(duì)象(在代碼執(zhí)行完后的調(diào)用將會(huì)始終返回{value: undefined, done: true})。
next總會(huì)返回一個(gè)對(duì)象,包含兩個(gè)屬性值:
value:yield關(guān)鍵字后邊表達(dá)式的值
done :如果已經(jīng)沒(méi)有yield關(guān)鍵字了,則會(huì)返回true .
function * generator () { yield 5 return 6 } const gen = generator() console.log(gen.next()) // {value: 5, done: false} console.log(gen.next()) // {value: 6, done: true} console.log(gen.next()) // {value: undefined, done: true} console.log(gen.next()) // {value: undefined, done: true} -- 后續(xù)再調(diào)用也都會(huì)是這個(gè)結(jié)果作為迭代器使用
Generator函數(shù)是一個(gè)可迭代的,所以,我們可以直接通過(guò)for of來(lái)使用它。
function * generator () { yield 1 yield 2 return 3 } for (let item of generator()) { item } // 1 // 2
return不參與迭代
迭代會(huì)執(zhí)行所有的yield,也就是說(shuō),在迭代后的Generator對(duì)象將不會(huì)再返回任何有效的值
我們可以在迭代器對(duì)象上直接調(diào)用return(),來(lái)終止后續(xù)的代碼執(zhí)行。
在return后的所有next()調(diào)用都將返回{value: undefined, done: true}
function * generator () { yield 1 yield 2 yield 3 } const gen = generator() gen.return() // {value: undefined, done: true} gen.return("hi") // {value: "hi", done: true} gen.next() // {value: undefined, done: true}Method: throw()
在調(diào)用throw()后同樣會(huì)終止所有的yield執(zhí)行,同時(shí)會(huì)拋出一個(gè)異常,需要通過(guò)try-catch來(lái)接收:
function * generator () { yield 1 yield 2 yield 3 } const gen = generator() gen.throw("error text") // Error: error text gen.next() // {value: undefined, done: true}Yield的語(yǔ)法
yield的語(yǔ)法有點(diǎn)像return,但是,return是在函數(shù)調(diào)用結(jié)束后返回結(jié)果的
并且在調(diào)用return之后不會(huì)執(zhí)行其他任何的操作
function method (a) { let b = 5 return a + b // 下邊的兩句代碼永遠(yuǎn)不會(huì)執(zhí)行 b = 6 return a * b } method(6) // 11 method(6) // 11而yield的表現(xiàn)則不一樣
function * yieldMethod(a) { let b = 5 yield a + b // 在執(zhí)行第二次`next`時(shí),下邊兩行則會(huì)執(zhí)行 b = 6 return a * b } const gen = yieldMethod(6) gen.next().value // 11 gen.next().value // 36yield*
yield*用來(lái)將一個(gè)Generator放到另一個(gè)Generator函數(shù)中執(zhí)行。
有點(diǎn)像[...]的功能:
function * gen1 () { yield 2 yield 3 } function * gen2 () { yield 1 yield * gen1() yield 4 } let gen = gen2() gen.next().value // 1 gen.next().value // 2 gen.next().value // 3 gen.next().value // 4yield的返回值
yield是可以接收返回值的,返回值可以在后續(xù)的代碼被使用
一個(gè)詭異的寫法
function * generator (num) { return yield yield num } let gen = generator(1) console.log(gen.next()) // {value: 1, done: false} console.log(gen.next(2)) // {value: 2, done: false} console.log(gen.next(3)) // {value: 3, done: true }
我們?cè)谡{(diào)用第一次next時(shí)候,代碼執(zhí)行到了yield num,此時(shí)返回num
然后我們?cè)僬{(diào)用next(2),代碼執(zhí)行的是yield (yield num),而其中返回的值就是我們?cè)?b>next中傳入的參數(shù)了,作為yield num的返回值存在。
以及最后的next(3),執(zhí)行的是這部分代碼return (yield (yield num)),第二次yield表達(dá)式的返回值。
上邊的所有示例都是建立在已知次數(shù)的Generator函數(shù)上的,但如果你需要一個(gè)未知次數(shù)的Generator,僅需要?jiǎng)?chuàng)建一個(gè)無(wú)限循環(huán)就夠了。
一個(gè)簡(jiǎn)單的隨機(jī)數(shù)生成比如我們將實(shí)現(xiàn)一個(gè)隨機(jī)數(shù)的獲?。?/p>
function * randomGenerator (...randoms) { let len = randoms.length while (true) { yield randoms[Math.floor(Math.random() * len)] } } const randomeGen = randomGenerator(1, 2, 3, 4) randomeGen.next().value // 返回一個(gè)隨機(jī)數(shù)代替一些遞歸的操作
那個(gè)最著名的斐波那契數(shù),基本上都會(huì)選擇使用遞歸來(lái)實(shí)現(xiàn)
但是再結(jié)合著Generator以后,就可以使用一個(gè)無(wú)限循環(huán)來(lái)實(shí)現(xiàn)了:
function * fibonacci(seed1, seed2) { while (true) { yield (() => { seed2 = seed2 + seed1; seed1 = seed2 - seed1; return seed2; })(); } } const fib = fibonacci(0, 1); fib.next(); // {value: 1, done: false} fib.next(); // {value: 2, done: false} fib.next(); // {value: 3, done: false} fib.next(); // {value: 5, done: false} fib.next(); // {value: 8, done: false}與async/await的結(jié)合
再次重申,我個(gè)人不認(rèn)為async/await是Generator的語(yǔ)法糖。。
如果是寫前端的童鞋,基本上都會(huì)遇到處理分頁(yè)加載數(shù)據(jù)的時(shí)候
如果結(jié)合著Generator+async、await,我們可以這樣實(shí)現(xiàn):
async function * loadDataGenerator (url) { let page = 1 while (true) { page = (yield await ajax(url, { data: page })) || ++page } } // 使用setTimeout模擬異步請(qǐng)求 function ajax (url, { data: page }) { return new Promise((resolve) => { setTimeout(_ => { console.log(`get page: ${page}`); resolve() }, 1000) }) } let loadData = loadDataGenerator("get-data-url") await loadData.next() await loadData.next() // force load page 1 await loadData.next(1) await loadData.next() // get page: 1 // get page: 2 // get page: 1 // get page: 2
這樣我們可以在簡(jiǎn)單的幾行代碼中實(shí)現(xiàn)一個(gè)分頁(yè)控制函數(shù)了。
如果想要從加載特定的頁(yè)碼,直接將page傳入next即可。
Generator還有更多的使用方式,(實(shí)現(xiàn)異步流程控制、按需進(jìn)行數(shù)據(jù)讀?。?br>個(gè)人認(rèn)為,Generator的優(yōu)勢(shì)在于代碼的惰性執(zhí)行,Generator所實(shí)現(xiàn)的事情,我們不使用它也可以做到,只是使用Generator后,能夠讓代碼的可讀性變得更好、流程變得更清晰、更專注于邏輯的實(shí)現(xiàn)。
如果有什么不懂的地方 or 文章中一些的錯(cuò)誤,歡迎指出參考資料
Javascript (ES6) Generators?—?Part I: Understanding Generators
What are JavaScript Generators and how to use them
文章示例代碼
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/95009.html
摘要:本文適合的讀者現(xiàn)在在手淘,京東,今日頭條,美柚等過(guò)億用戶的手機(jī)中的,都常見(jiàn)網(wǎng)頁(yè),他們有更新快,靈活,便于分享和傳播的特性。這里有他們中的幾個(gè)的例子手淘,美柚。 本文適合的讀者??????? 現(xiàn)在在手淘,京東,今日頭條,美柚等過(guò)億用戶的手機(jī)app中的,都常見(jiàn)h5網(wǎng)頁(yè),他們有更新快,靈活,便于分享和傳播的特性。這里有他們中的幾個(gè)h5的例子:(手淘,美柚)。這些app中都嵌者數(shù)以百計(jì),千計(jì)的...
摘要:本文適合的讀者現(xiàn)在在手淘,京東,今日頭條,美柚等過(guò)億用戶的手機(jī)中的,都常見(jiàn)網(wǎng)頁(yè),他們有更新快,靈活,便于分享和傳播的特性。這里有他們中的幾個(gè)的例子手淘,美柚。 本文適合的讀者??????? 現(xiàn)在在手淘,京東,今日頭條,美柚等過(guò)億用戶的手機(jī)app中的,都常見(jiàn)h5網(wǎng)頁(yè),他們有更新快,靈活,便于分享和傳播的特性。這里有他們中的幾個(gè)h5的例子:(手淘,美柚)。這些app中都嵌者數(shù)以百計(jì),千計(jì)的...
摘要:本文適合的讀者現(xiàn)在在手淘,京東,今日頭條,美柚等過(guò)億用戶的手機(jī)中的,都常見(jiàn)網(wǎng)頁(yè),他們有更新快,靈活,便于分享和傳播的特性。這里有他們中的幾個(gè)的例子手淘,美柚。 本文適合的讀者??????? 現(xiàn)在在手淘,京東,今日頭條,美柚等過(guò)億用戶的手機(jī)app中的,都常見(jiàn)h5網(wǎng)頁(yè),他們有更新快,靈活,便于分享和傳播的特性。這里有他們中的幾個(gè)h5的例子:(手淘,美柚)。這些app中都嵌者數(shù)以百計(jì),千計(jì)的...
摘要:回調(diào)函數(shù),一般在同步情境下是最后執(zhí)行的,而在異步情境下有可能不執(zhí)行,因?yàn)槭录](méi)有被觸發(fā)或者條件不滿足。同步方式請(qǐng)求異步同步請(qǐng)求當(dāng)請(qǐng)求開始發(fā)送時(shí),瀏覽器事件線程通知主線程,讓線程發(fā)送數(shù)據(jù)請(qǐng)求,主線程收到 一直以來(lái)都知道JavaScript是一門單線程語(yǔ)言,在筆試過(guò)程中不斷的遇到一些輸出結(jié)果的問(wèn)題,考量的是對(duì)異步編程掌握情況。一般被問(wèn)到異步的時(shí)候腦子里第一反應(yīng)就是Ajax,setTimse...
閱讀 3822·2023-04-25 19:07
閱讀 3571·2021-11-22 12:02
閱讀 3146·2021-10-12 10:11
閱讀 3934·2021-09-03 10:49
閱讀 2899·2019-08-30 13:21
閱讀 3011·2019-08-30 11:14
閱讀 2096·2019-08-29 15:40
閱讀 2881·2019-08-28 18:29