迭代器、生成器、async\await原理

165 阅读3分钟

通过js实现迭代器、可迭代对象、生成器,以及使用生成器理解async\await原理

迭代器

在 JavaScript 中,迭代器是一个对象,它定义一个序列,并在终止时可能返回一个返回值。 更具体地说,迭代器是通过使用 next() 方法实现 Iterator protocol 的任何一个对象,该方法返回具有两个属性的对象: value,这是序列中的 next 值;和 done ,如果已经迭代到序列中的最后一个值,则它为 true 。如果 value 和 done 一起存在,则它是迭代器的返回值。

// 实现一个arr的迭代器
const arr = ['a', 'b', 'c'];

const createArrIterator = () => {
  let index = 0;
  return {
    next() {
      if (index < arr.length) {
        return { done: false, value: arr[index++] };
      }

      return { done: true, value: undefined };
    },
  };
};

// test
const iterator = createArrIterator();

console.log(iterator.next());
console.log(iterator.next());
console.log(iterator.next());
console.log(iterator.next());

可迭代对象

若一个对象拥有迭代行为,比如在 for...of 中会循环哪些值,那么那个对象便是一个可迭代对象。一些内置类型,如 Array 或 Map 拥有默认的迭代行为,而其他类型(比如Object)则没有。
为了实现可迭代,一个对象必须实现  @@iterator 方法,这意味着这个对象(或其原型链中的任意一个对象)必须具有一个带 Symbol.iterator 键(key)的属性。

// 实现一个可迭代对象zoo
class Zoo {
  constructor(animals) {
    this.animals = animals;
  }

  [Symbol.iterator]() {
    let index = 0;
    return {
      next: () => {
        if (index < this.animals.length) {
          return { value: this.animals[index++], done: false };
        }
        return { value: undefined, done: true };
      },
      // 可以监听到break return
      return() {
        console.log('迭代结束');
        return { value: undefined, done: true };
      },
    };
  }
}

// test
const zoo = new Zoo(['cat', 'dog', 'tiger']);

for (const item of zoo) {
  if (item === 'dog') break;
  console.log(item);
}

生成器函数

虽然自定义的迭代器是一个有用的工具,但由于需要显式地维护其内部状态,因此需要谨慎地创建。生成器函数提供了一个强大的选择:它允许你定义一个包含自有迭代算法的函数, 同时它可以自动维护自己的状态。 生成器函数使用 function*语法编写。 最初调用时,生成器函数不执行任何代码,而是返回一种称为Generator的迭代器。 通过调用生成器的下一个方法消耗值时,Generator函数将执行,直到遇到yield关键字。

// 返回值:yield value
// 传参:由上一阶段yield返回  value = yield,注意:第一段传参通过函数传
// return方法:提前结束,可以传参到value { value: 123, done: true }
// thorw方法:抛出异常,如果在上一阶段yield有捕获异常处理,后续代码将正常执行
const createGenerator = function* (param1) {
  console.log(param1); // p1
  const param2 = yield 10;
  console.log(param2); // p2
};
const generator = createGenerator('p1');
console.log(generator.next()); // { value: 10, done: false }
console.log(generator.next('p2')); // { value: undefined, done: true }

通过生成器函数简化迭代器

yield* 表达式用于委托给另一个generator 或可迭代对象。

class Zoo {
  constructor(animals) {
    this.animals = animals;
  }
  // 自动帮你迭代可迭代对象
  *[Symbol.iterator]() {
    yield* this.animals;
  }
}
// test
const zoo = new Zoo(['cat', 'dog', 'tiger']);

for (const item of zoo) {
  console.log(item);
}

async\await原理

// 模拟两个需按序执行的接口请求

const login = function () {
  return new Promise(resolve => {
    setTimeout(() => {
      resolve('LH1100');
    }, 1500);
  });
};

const getUserInfo = function (token) {
  return new Promise(resolve => {
    setTimeout(() => {
      resolve({
        name: 'lh',
        token,
      });
    }, 1500);
  });
};

function* reqGenerator() {
  const token = yield login();
  console.log('token', token);
  const userInfo = yield getUserInfo(token);
  console.log('userInfo', userInfo);
}

// 手动迭代
// const reqGen = reqGenerator();
// reqGen.next().value.then(res => {
//   reqGen.next(res).value.then(res => {
//     reqGen.next(res);
//   });
// });

// 封装自动迭代函数
const execGen = genFn => {
  const gen = genFn();
  function exec(res) {
    const { done, value } = gen.next(res);
    if (!done) {
      value.then(res => exec(res));
    }
  }
  exec();
};

execGen(reqGenerator);

image.png