成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

Generator的正確打開方式

happyfish / 732人閱讀

摘要:不參與迭代迭代會(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ì)得到一條cookinglog,
然后在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è)屬性值:
valueyield關(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ì)再返回任何有效的值

Method: return()

我們可以在迭代器對(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 // 36
yield*

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 // 4
yield的返回值

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í)際的使用場(chǎng)景

上邊的所有示例都是建立在已知次數(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+asyncawait,我們可以這樣實(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

相關(guān)文章

  • multipages-generator今日發(fā)布?!媽媽再也不用擔(dān)心移動(dòng)端h5網(wǎng)站搭建了!

    摘要:本文適合的讀者現(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ì)的...

    xavier 評(píng)論0 收藏0
  • multipages-generator今日發(fā)布?!媽媽再也不用擔(dān)心移動(dòng)端h5網(wǎng)站搭建了!

    摘要:本文適合的讀者現(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ì)的...

    Kerr1Gan 評(píng)論0 收藏0
  • multipages-generator今日發(fā)布?!媽媽再也不用擔(dān)心移動(dòng)端h5網(wǎng)站搭建了!

    摘要:本文適合的讀者現(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ì)的...

    doodlewind 評(píng)論0 收藏0
  • 淺析JavaScript異步

    摘要:回調(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...

    Tangpj 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<