likes
comments
collection
share

Generator 函数的语法 | es6基础

作者站长头像
站长
· 阅读数 12

先来看看这个函数内部有啥

  function * fun1() {
    yield 1;
  }
  console.log(fun1());

Generator 函数的语法 | es6基础

Generator是es6提供的一种异步编程解决方案,是一个状态机,封装了多个内部状态。 两个特征:

  • function关键字和函数名之间有个*星号
  • 函数内部使用yield表达式
  function * fun1() {
    yield 1;
    yield 2;
    return 3;
  }
  let f = fun1();
  console.log(f.next());
  console.log(f.next());
  console.log(f.next());
  • 函数调用的时候并不执行,返回一个指向内部状态的指针对象(遍历器对象)
  • 执行next方法,指针就会移向下一个状态
  • done属性表示遍历是否结束
  • 函数结尾没有返回return,则最后一个状态的value是undefined

Generator 函数的语法 | es6基础

介绍

yeild表达式

只有调用next方法才会遍历下一个状态,所以就是提供了一个可以暂停执行的函数。

  • 惰性求值:只有调用到next方法,内部指针指向该语句才会执行
  • generator函数可以变成一个暂缓执行函数
  function * fun1() {
    function name() {
      console.log(1);
    }
    return name();
  }
  let f = fun1();
  setTimeout(() => {
    f.next();
  }, 2000);
  • 可以直接使用在即时函数中
 let fun1 = (function* () {
  yield 1;
 })();

和Iterator接口的关系

执行generator函数实际上是返回一个遍历器,就可以赋值给对象的Symbol.iterator属性

  function * fun1() {
    yield 1;
    yield 2;
    yield 3;
    return 4;
  }
  let o = {};
  o[Symbol.iterator] = fun1;
  console.log(...o);  // 1 2 3 

next方法的参数

  • next方法的参数表示上一个yield表达式的返回值,第一次使用next的参数是无效的
function* foo(x) {
  let y = 2 * (yield (x + 1));
  let z = yield (y / 3);
  return (x + y + z);
}

let a = foo(1);
console.log(a.next()); // {value: 2, done: false}
// 传入的1,next返回x+1

console.log(a.next(2)); // {value: 1.3333333333333333, done: false}done: falsevalue: 1.3333333333333333[[Prototype]]: Object
// 传入2作为x+1的值,y=4.返回y/3

console.log(a.next(3)); // {value: 8, done: true}
// 传入3作为y/3的值,z=3
// 此时,x=1,y=4,z=4

使用for...of循环

function * foo () {
  yield 1;
  yield 2;
  yield 3;
  yield 4;
  yield 5;
  yield 6;
  return 7;
}
for (let v of foo()) {
  console.log(v);
}
  • next返回对象的done属性为true,最后一个7不会包含在循环中
  • 使用这个特性去遍历一个对象的所有属性
  function* objectEntries(obj) {
    let keys = Reflect.ownKeys(obj);
    for (let key of keys) {
      yield [key, obj[key]]
    }
  }
  let jane = { first: 'Jane', last: 'Doe' };
  for (let [key, value] of objectEntries(jane)) {
    console.log(`${key}: ${value}`);
  }

Generator.prototype.throw

  • 可以在函数外部抛出错误,在函数内部捕获
  function* fun1() {
    try {
      yield 1;
    } catch (e) {
      console.log('内部捕获', e);
    }
  }
  let f = fun1();
  f.next();
  try {
    f.throw('a'); // fun1执行catch
    f.throw('b'); // 第二次抛出,fun1不会再捕获了,向外层继续抛出,被函数体外的catch捕获
  } catch (e) {
    console.log('外部捕获', e);
  }

Generator 函数的语法 | es6基础

  • 即使报错了捕获了,也会执行catch后面的yield,跳过catch前面的yield
  function* fun1() {
    try {
      yield 1;
      yield 2;
    } catch (e) {
      console.log('内部捕获', e);
    }
    yield 3;
  }
  let f = fun1();
  console.log(f.next());
  console.log(f.throw(new Error()));
  console.log(f.next());

Generator 函数的语法 | es6基础

  • generator函数内部抛出错误但是没有被内部catch的话,就直接报错,不会再执行下去了,如果再调用next方法就会返回{value:undefined, done:true},即被认为已经执行结束了

Generator.prototype.return

  • 可以提前终结generator函数
function* foo() {
  yield 1;
  yield 2;
  yield 3;
  yield 4;

}

let f = foo();
console.log(f.next());
console.log(f.return(9));
console.log(f.next());

Generator 函数的语法 | es6基础

  • 如果内部函数有try...finally..,会先执行finally再执行return

yield* 表达式

  • 一个 Generator 函数里面执行另一个 Generator 函数
function * fun1() {
  yield 1;
  yield 2;
  yield 3;
} 
function * fun2() {
  yield 4;
  yield * fun1();
  yield 5;
}
for(let v of fun2()) {
  console.log(v);
}
// 4 1 2 3 5
  • 其他形式
yield * [1, 2, 3, 4];
yield * "hello"; // “hello” 字符串遍历器

 let fun1 = (function* () {
  yield * "hello"
 })()
 console.log(fun1.next()); // {value: 'h', done: false}
  • 应用
// 打平数组
  function* iterTree(tree) {
    if (Array.isArray(tree)) {
      for (let i = 0; i < tree.length; i++) {
        yield * iterTree(tree[i]);
      }
    } else {
      yield tree;
    }
  }
  const tree = [1, 2, [3,4,[5,6],7,8], [9, 10]]
  console.log(...iterTree(tree));
  // 1 2 3 4 5 6 7 8 9 10

参考文章: # Generator 函数的语法