迭代器和生成器的使用

迭代器

使用

let arr = [1, 2, 3, 4];
let iter = arr[Symbol.iterator];
console.log(iter); // ƒ values() { [native code] }

// 执行这个方法
iter = arr[Symbol.iterator]();
console.log(iter); // Array Iterator {}
console.log(iter.next()); // {value: 1, done: false}
console.log(iter.next()); // {value: 2, done: false}
console.log(iter.next()); // {value: 3, done: false}
console.log(iter.next()); // {value: 4, done: false}
console.log(iter.next()); // {value: undefined, done: true}
console.log(iter.next()); // {value: undefined, done: true}

模拟实现

function makeIterator(array) {
  let nextIndex = 0;
  return {
    next: function() {
      return nextIndex < array.length
        ? { value: array[nextIndex++], done: false }
        : { value: undefined, done: true };
    }
  }
}

let iter = makeIterator([1, 2, 3, 4]);
console.log(iter.next()); // {value: 1, done: false}
console.log(iter.next()); // {value: 2, done: false}
console.log(iter.next()); // {value: 3, done: false}
console.log(iter.next()); // {value: 4, done: false}
console.log(iter.next()); // {value: undefined, done: true}

for…of

ES6借鉴了c++,java,c#,python,这些语言里都存在for...of循环,这个实际上就是一个迭代的过程。javascript中,for...of就是调用了Symbol.iterator这个接口。

// 数组
let arr = [1, 2, 3, 4];
for(let i of arr) {
  console.log(i);
}
// 1
// 2
// 3
// 4

// 对象
let obj = {
	start: [1, 2, 3, 4],
  end: [5, 6, 7]
}
//没有部署Symbol.iterator的情况下,进行迭代
for(let i of obj) { // Uncaught TypeError: obj is not iterable
	console.log(i);
}

// for...in 是用来遍历对象的
for(let i in arr) {
	console.log(i);
}
// 0
// 1
// 2
// 3

只有部署了迭代器接口Symbol.iteratorfor...of才能遍历。 Symbol.iterator对数据读取是一种有序的,连续的,基于拉取的方式,而对象数据的组织方式是无序的,所以要把对象排除在外。通过部署Symbol.iterator,可以实现让for...of迭代对象。

部署Symbol.iterator

let obj = {
  start: [1, 2, 3, 4],
  end: [5, 6, 7],
  [Symbol.iterator]() {
    let index = 0;
    let arr = [...this.start, ...this.end];
    let len = arr.length;

    return {
      next() {
        return index < len
        ? { value: arr[index++], done: false }
        : { value: undefined, done: true }
      }
    }
  }
};

for (const i of obj) {
  console.log(i); // 1 2 3 4 5 6 7
}

tips

  1. 部署了Symbol.iterator接口的对象,可以使用...运算符展开。

  2. 调用了Symbol.iteratornext()方法,到{value: undefined, done: true}后,再使用for...of遍历可能会失效。

生成器

yield关键字

function * test() {
  yield 'a';
  yield 'b';
  yield 'c';
  return 'd';
}

// 返回生成器对象, yield:产出一个值, 暂停函数运行
const gen = test();
console.log(gen.next()); // {value: "a", done: false}
console.log(gen.next()); // {value: "b", done: false}
console.log(gen.next()); // {value: "c", done: false}
console.log(gen.next()); // {value: "d", done: true} yield产出有值为false,产出没值为true
console.log(gen.next()); // {value: undefined, done: true}
// yield 暂停(记忆功能),只能出现在生成器函数中
// return 结束函数执行

yield返回值的传入

function * test() {
  let a = yield 'a';
  console.log(a);
  yield 'b';
  yield 'c';
  return 'd';
}

let gen = test();
console.log(gen.next());   // {value: "a", done: false}
console.log(gen.next(10)); // 10 {value: "b", done: false}

yield单独成立方式

function * demo() {
  // yield
  console.log('hello' + (yield)); // yield不单独成立,要成立需加()
}

let gen = demo();
console.log(gen.next());	// {value: undefined, done: false}
function * demo() {
  // yield
  console.log('hello' + (yield 123));
}

let gen = demo();
console.log(gen.next());	// {value: 123, done: false}

yield作为函数参数

function * demo() {
  foo(yield 'a', yield 'b');
}

function foo(a, b) {
  console.log(a, b);
}

let gen = demo();
console.log(gen.next()); // {value: "a", done: false}
console.log(gen.next()); // {value: "b", done: false}
console.log(gen.next()); // undefined undefined {value: undefined, done: true}

使用for…of遍历生成器对象

function * foo() {
  yield 1;
  yield 2;
  yield 3;
  yield 4;
  yield 5;
  yield 6;
  return 7;
}

for(let i of foo()) {
	console.log(i); // 1 2 3 4 5 6
}

接收yield的产出值

function * foo() {
  let value1 = yield 1;
  console.log('value1: ' + value1);
  
  let value2 = yield 2;
  console.log('value2: ' + value2);
  
  let value3 = yield 3;
  console.log('value3: ' + value3);
  
  let value4 = yield 4;
  console.log('value4: ' + value4);
}

// 蛇形的传值方式
let gen = foo();
console.log(gen.next());				// 								 {value: 1, done: false}
console.log(gen.next('two'));   // value1: two     {value: 2, done: false}
console.log(gen.next('three')); // value2: three   {value: 3, done: false}
console.log(gen.next('four'));  // value3: four    {value: 4, done: false}
console.log(gen.next('five'));  // value4: five    {value: undefined, done: true}

优化Symbol.iterator

let obj = {
  start: [1, 2, 3],
  end: [7, 8, 9],
  [Symbol.iterator]: function * () {
    let index = 0;
    let arr = [...this.start, ...this.end];
    let len = arr.length;

    while(index < len) {
      yield arr[index++];
    }
  }
}

for (let i of obj) {
  console.log(i); // 1 2 3 7 8 9
}
let obj = {
  a: 1,
  b: 2,
  c: 3,
  [Symbol.iterator]: function * () {
    let index = 0;
    let map = new Map();
    for (let [key, value] of Object.entries(this)) {
      map.set(key, value);
    }
    let mapEntries = [...map.entries()];

    while(index < mapEntries.length) {
      yield mapEntries[index++];
    }
  }
}

for(let i of obj) {
	console.log(i);
}
// [ 'a', 1 ]
// [ 'b', 2 ]
// [ 'c', 3 ]

模拟实现async/await

function Co (generator) {
  return new Promise((resolve, reject) => {
    const next = (data) => {
      const { value, done } = generator.next(data);

      if (done) {
        resolve(data);
      } else {
        value.then(val => next(val), reject);
      }
    };
    next();
  });
}

const p1 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve(1);
  }, 1000);
});

const p2 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve(2);
  }, 1000);
});

function * test() {
  const a = yield p1;
  const b = yield p2;
  console.log(`a: ${a}, b: ${b}`);
}

Co(test()); // a: 1, b: 2

> cd ..