天天看點

學習筆記:javascript中的Generator函數

最近在學習redux-saga,由于redux-saga需要使用Generator函數,是以下來就回顧了一下Generator

Generator 函數是 ES6 提供的一種異步程式設計解決方案,文法行為與傳統函數完全不同

Generator函數與普通函數沒什麼差別,隻是在

function

關鍵字後面多了一個

*

号,函數内部多了一個

yield

關鍵字

function* demo() {
  console.log(1)
  yield 111
  console.log(2)
  yield 222
  console.log(3)
  return 333
}           

上面的函數即為一個Generator函數,Generator調用後并不會直接執行,而是傳回一個指向内部狀态的指針對象需要調用該指針對象的

next

方法才會向下執行,當遇到

yield

關鍵字的時候,函數會挂起,交給外部,直到遇到下一次調用

next

方法

let g = demo()

此時若我們第一次調用

let val1 = g.next()

// 列印 1,val1 = {value: 111, done: false}

let val2 = g.next()
// 列印 2,val2 = {value: 222, done: false}

let val3 = g.next()
// 列印 3,val3 = {value: 333, done: true}           

根據結果我們可以很清楚的看到當傳回值的

done

true

的時候就代表Generator執行完畢,此外Generator函數還可以被循環

for (let val of demo()) {
  console.log(val)
}
// val依次會列印出 111、222,注意當done為true的時候不會列印value的值           

當然Generator在工作中最常用到的就是處理異步函數,下面舉個例子:

function demo(a, b) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve(a + b)
    }, 1000)
  })
}


function* gen(val) {
  let a = yield demo(1, 2)
  console.log(a)
  let b = yield '123'
  return b
}
           

這裡需要注意的是,變量

a

的值是需要

next

方法傳入的

let g = gen()
let {value} = g.next()           

此時函數中的變量a的值就是再次調用

next

傳入的值, 比如

g.next(value)

,則此時a的值就是一個Promise的執行個體,顯然這不是我們想要的,我們期望拿到的是

Promise.resolve

的值

那麼根據上面的結果我們可以編寫一個Generator執行器

function execGen(gen) {
  let g = gen()

  function deep(val) {
    let {value, done} = g.next(val)
    if (done) {
      return
    }
    if (value instanceof Promise) {
      value.then(data => {
        deep(data)
      })
    } else {
      deep(value)
    }
  }
  deep()
}

execGen(gen)

// gen函數的console會正确的列印出值3
           

其實如果不是redux-saga的話我感覺我工作中基本不會用到Generator函數,個人感覺更優雅的是async/await 來處理異步情況

async function handler() {
  try{
    let val = await demo()
  }catch (e) {
    
  }
}           

最後還是期望有大佬能夠幫忙指出相比于async/await,Generator有什麼優勢^_^