Es6 generator淺入淺出

生成器

生成器是特殊的函數。生成器函數在執行時能暫停,後面又能從暫停處執行。 show me the codingnode

function* zcGenerator() { // 這就是生成器
    yield 'ak-47'
}
複製代碼

迭代器

const zcInterator = zcGenerator()
複製代碼

調用生成器並不會執行函數,而是會返回一個迭代器,迭代器用來控制生成器的執行。調用迭代器的next()方法,會向生成器請求一個值,生成器內的語句執行到第一個yield的位置,並返回yield後跟的值,此時生成器異步掛起執行,直到下次再請求值,生成器恢復執行,如此循環下去,直到最後生成器執行完畢。 next()方法返回一個對象{value: '', done: ''}, value表示本次yield返回的值,done表示生成器後續時候仍是有yield語句,即生成器是否執行完畢。
從執行上下文的角度來理解迭代器,建立迭代器的時候,生成器入棧,初始化參數(若是有的話),執行完畢後出棧,而且銷燬當前執行環境,可是迭代器仍然保存着對他的引用,因此該環境並不會銷燬,這個東西和閉包很像。換個角度來講,生成器須要恢復執行,因此環境不能銷燬,保存在迭代器上。bash

function* weaponGenerator() {
  yield 'ak47'
  yield 'm16'
}
const weaponIterator = weaponGenerator()

console.log(weaponIterator.next()) // {value: 'ak47', done: false}
console.log(weaponIterator.next()) // {value: 'm16', done: false}
console.log(weaponIterator.next()) // {value: undefined, done: true}
複製代碼

使用yield* 表示將執行權交到另外一個生成器函數(當前函數暫停執行)閉包

function* weaponGeneratorInner() {
  yield 'g56'
  yield 'h58'
}

function* weaponGenerator() {
  yield 'ak47'
  yield* weaponGeneratorInner()
  yield 'm16'
}
const weaponIterator = weaponGenerator()

console.log(weaponIterator.next()) // {value: 'ak47', done: false}
console.log(weaponIterator.next()) // {value: 'g56', done: false}
console.log(weaponIterator.next()) // {value: 'h58', done: false}
console.log(weaponIterator.next()) // {value: 'm16', done: false}
console.log(weaponIterator.next()) // {value: undefined, done: true}
複製代碼

迭代器與生成器交互

交互方式有三種dom

  1. 向生成器傳遞參數與生成器交互,像普通函數同樣傳遞參數。
function* parGenerator(weapon) {
    yield weapon
}

const parIterator = parGenerator('j-20')
console.log(parIterator.next()) // { value: 'j-20', done: false }
複製代碼
  1. 經過向next()傳遞參數,與生成器交互 next()的參數會傳遞給當前生成器正在yield的值,所以,第一次調用next(),生成器並無處於掛起的值,因此傳遞參數沒有任何意義,調用完第一個next()方法,生成器開始執行,碰見第一個yield,函數掛起,而且返回當前值;第二次調用next('j-30'),j-30會替換掉當前yield的值j-20,並繼續往下執行,將newWeapon賦值爲j-30,生成器執行完畢。
function* parGenerator(weapon) {
  const newWeapon = yield weapon
  return newWeapon
}

const parIterator = parGenerator('j-20')
console.log(parIterator.next()) // { value: 'j-20', done: false }
console.log(parIterator.next('j-30')) // { value: 'j-30', done: true }
複製代碼

注意:由於經過next()傳遞參數只能傳遞給當前生成器yield的值,所以第一次調用next()傳遞參數沒有意義,因此,若是想爲生成器提供一個初始值,能夠向生成器函數穿第一個參數做爲初始值。異步

  1. 經過迭代器的throw方法與生成器交互
function* throwGenerator() {
  try {
    yield 'good'
  } catch (err) {
    console.log(err)
  }
}

const throwInterator = throwGenerator()
console.log(throwInterator.next())
console.log(throwInterator.throw('bad'))
複製代碼

thrownext傳參相似,只能對當前生成器yield的值throw,所以,在建立迭代器以後當即調用throw沒有意義(會報錯),換個角度來講,只有代碼執行的時候才能夠try-catch,在throw以前沒有執行生成器,try-catch也就沒什麼用。函數

注意:throw必須在next以後調用,由於在調用第一個next以後,生成器在第一個yield處掛起,只有在請求下一個值或者throw的時候纔會繼續日後執行try...catch...,在throw的時候,生成器從新入棧,從yield 'good'的地方繼續日後執行,生成器會將yield 'good'做爲一個錯誤拋出,被catch抓住。ui

實例

  1. 生成id
function* idGenerator() {
  let id = 0
  while (true) {
    yield ++id
  }
}
const idIterator = idGenerator()

const id1 = idIterator.next().value
console.log(id1)
const id2 = idIterator.next().value
console.log(id2)
複製代碼

由於yield會暫時掛起函數執行,因此while不會無限循環 2. 遍歷dom節點spa

function* elementGenerator(element) {
  yield element
  element = element.firstElementChild
  while (element) {
    elementGenerator(element)
    element = element.nextElementSibling
  }
}

for (let elementGeneratorElement of elementGenerator()) {
  console.log(elementGeneratorElement.nodeName)
}
複製代碼
相關文章
相關標籤/搜索