Skip to content

Generator

语法

js
function* generator() {
  yield 1;
  yield 2;
  yield 3;
}

const gen = generator()

console.log(gen.next()) // { value: 1, done: false }
console.log(gen.next()) // { value: 2, done: false }
console.log(gen.next()) // { value: 3, done: false }

TIP

Generator 函数是 ES6 提供的一种异步编程解决方案, 执行 Generator 函数会返回一个可迭代对象(Iterator Object), Iterator的主要方法是next(), next() 方法返回一个包含属性 done 和 value 的对象。你也可以通过向 next 方法传入一个参数来向生成器(generator)传一个值.

调用遍历器对象的next方法,使得指针移向下一个状态。也就是说,每次调用next方法,内部指针就从函数头部或上一次停下来的地方开始执行,直到遇到下一个yield表达式(或return语句)为止。换言之,Generator 函数是分段执行的,yield表达式是暂停执行的标记,而next方法可以恢复执行.

给对象自定义迭代器对象

js中对象不可被for...of遍历, 我们可以给对象实现一个迭代器函数,供for..of调用。 提供一个 generator 函数作为 Symbol.iterator,来使用 generator 进行迭代。

之所以代码正常工作,是因为 range[Symbol.iterator]() 现在返回一个 generator,而 generator 方法正是 for..of 所期望的:

  • 它具有 .next() 方法
  • 它以 {value: ..., done: true/false} 的形式返回值
js
const range = {
  from: 1,
  to: 5,
  *[Symbol.iterator]() { // [Symbol.iterator]: function*() 的简写形式
    for(let value = this.from; value <= this.to; value++) {
      yield value
    }
  }
}

for(let v of range) {
  console.log(v, 'range')
}
// 1 range
// 2 range
// 3 range
// 4 range
// 5 range

参考资料

async/await

promise解决了遗留已久的“回调地狱”的问题,但是当链式调用过多时,我们似乎又陷入了另一种麻烦里,看起来也不是很优雅、直观,因此async/await这种解决方法应运而生。它让我们能够更愉快的使用promise。

async函数

语法:

js
/* 隐式 */
async function fn() {
  return 1
}
/* 显示 */
async function fn2() {
  return new Promise(resolve => resolve(2))
}

fn().then(res => {
  console.log(res) // 1
})
fn2().then(res => {
  console.log(res) // 2
})

在函数前加上async关键字意味着函数总会返回一个promise。如果函数没有显示的返回promise,js引擎也会隐式的将其封装到一个promise中,如果函数有返回值,这个值就会被resolved。

await关键字

await 关键字会使js引擎等待,直到 promise 得到解决并返回其结果。await必须且只能在async函数中使用。

js
async function fn() {
  const promise = new Promise(resolve => {
    setTimeout(() => {
      resolve('done')
    }, 2000)
  })
  const res = await promise
  console.log(res) // 2秒后打印 done
  console.log('start')
}
fn()

处理错误:如果promise没有正常的resolve,就会被rejected,抛出错误(类似throw语句),此时await就不会返回结果,需要我们去处理错误。

js
/**
 * 一般我们会用try/catch处理
 * 处理同步错误: await会同步执行代码
 */
async function fn() {
  const promise = Promise.reject(new Error('Error: error~'))
  try {
    await promise 
    /*与 throw new Error('error~') 同理*/
  } catch (error) {
    console.log(error) // Error: error
  }
}
fn()

/* 或者用.catch() */
async function fn2() {
  return Promise.reject(new Error('error---'))
}
fn2().catch(err => {
  console.log(err) // Error: error---
})

另外:一个async函数中,能够有多个await执行。

使用Promise 和 Generator 实现async函数

TIP

实际上,async函数就是利用PromiseGenerator 实现的,可以理解为js在语言层面提供的语法糖。

模拟实现

Details
js
function asyncToGenerator(genFn) {
  //把返回值包装成promise
  return new Promise((resolve, reject) => {
    var gen = genFn()

    function step(val) {
      //错误处理
      try {
        var next = gen.next(val) 
      } catch(err) {
        return reject(err)
      }
      // 执行结束则退出执行函数,返回最终执行结果
      if(next.done) {
        return resolve(next.value)
      }
      //next.value需包装为promise,以兼容yield后面是非promise的情况(如基本类型)
      Promise.resolve(next.value).then(
        val => {
          console.log(val, 'step then')
          step(val)
        }, 
        err => {
          //抛出错误,供genFn函数内捕获错误
          console.log(err, 'step catch')
          gen.throw(err)
        })
    }
    step()
  })
}

// test case
function* gen() {
  try {
    yield Promise.resolve(1)
    yield Promise.resolve(2)
    yield Promise.reject('yield error')
    yield Promise.resolve(4)
  } catch (error) {
    console.log(error, 'gen catch')
  }
}
asyncToGenerator(gen).then(res => {
  console.log(res)
})
// log
// 1 step then
// 2 step then
// yield error step catch
// yield error gen catch

Updated at: