迭代器(iterator)、生成器(generator)

初次邂逅

  • 迭代器:具备遍历的功能;
  • 一般可迭代对象具有Symbol.iterator属性
  • 可迭代(可被遍历)的内置对象:数组(# Array.prototype[Symbol.iterator]())、字符串(String.prototype[Symbol.iterator]())等等;
let arr = [1,2,3]
for(let item of arr){
    console.log(item)
}

let str = 'lxx'
for(let char of arr){
    console.log(char)
}
  • 自定义可遍历对象,具备Symbol.iterator属性
let obj = {
  items: [6, 7, 8],
  [Symbol.iterator]() {
    let idx = 0;
    let next = () => {
      if (idx < this.items.length) {
        return { value: this.items[idx++], done: false };
      } else {
        return { done: true };
      }
    };
    return { next };
  },
};
// 遍历对象时会调用自动调用Symbol.iterator方法
for (let item of obj) {
  console.log(item);
}
  • 生成器函数,有*标识并且和关键字yield搭配使用,可用来控制函数的执行,可暂停函数的执行
// 相当于gen函数中有三个待执行的任务,希望挨个进行执行处理
function* gen() {
  yield 1;
  yield 2;
  yield 3;
}
let it = gen()
console.log(it)
  • 调用生成器函数生成迭代器对象,具有nextreturnthrow等方法
  • 调用一次next方法,就会执行一个yield,有nyield,则需要调用n + 1next
  • 每调用一次next时,执行的代码内容(范围)是上边的代码到yield右侧(不包含左边的赋值)
let res1 = it.next();
console.log(res1); // {value: 1, done: false}, value相当于是yield产生的值,done代表是否已经迭代完

let res2 = it.next();
console.log(res2); // {value: 2, done: false}

let res3 = it.next();
console.log(res3); // {value: 3, done: false}

let res4 = it.next();
console.log(res4); // {value: 3, done: false}
  • 除第一个调用的next之外,后面调用时传递的参数,都会传(赋值)给相应位置的yield左侧的变量
function* gen() {
  let r1 = yield 1;
  console.log(r1); // b
  let r2 = yield 2;
  console.log(r2); // c
  let r3 = yield 3;
  console.log(r3); // d
}
let it = gen()
it.next('a');
it.next('b');
it.next('c');
it.next('d');
  • 因为生成器函数的执行结果是个迭代器对象,所以可以使用for...of进行遍历,得到的每一项是yield产生的值,有几个yield就会循环几项
for(let item of it){
    console.log(item)
}
  • 因为生成器函数中不确定有几个yield(不确定有几个待执行的任务),但每执行完一个任务会知道是否所有任务已经完全执行完了,所以可以使用do...while来执行
let res;
do {
    res = it.next(); // {value: 1, done: false}
    console.log(res.value);
} while (!res.done);
  • 还可以自定义函数递归的来执行迭代器对象
function next() {
    // 可以拿到每一个任务的结果,及是否全部执行完了
    let { value, done } = it.next();
    if (done) {
      console.log(所有任务执行完毕");
    } else {
      console.log(value);
      next(); // 继续向后执行
    }
}
next();
  • yield产生异步任务
function* gen() {
    yield 1;
    yield new Promise((resolve) => {
      setTimeout(() => {
        resolve(2);
      }, 2000);
    });
    yield 3;
}
let it = gen(); // 调用生成器函数,产生迭代器对象
console.log(it); // 拥有next方法,控制函数的执行
for (let item of it) {
    console.log(item); // 迭代(遍历)迭代器对象产生:1,promise,3
}
  • 可以借助自定义迭代方法,检测出产生值的类型,进行特殊处理
function next() {
    let { value, done } = it.next();
    if (done) {
      console.log("函数执行完毕");
    } else {
      console.log(value);
      if (value instanceof Promise) {
        // 如果值是一个promise,就调用then方法 
        value.then((res) => {
          console.log(res);
          next(); // 处理完promise后再继续执行后续任务
        });
      } else {
        next(); // 产生的普通值,就直接进行后续的任务
      }
    }
}

温故而知新

  • 自定义发布订阅模型,将任务存储起来,供后面调用
class PubSub {
  constructor() {
    // 存储不同类型的任务(事件)
    // 数据格式:{event1: [fn1, fn2, ...], event2: [cb1, cb2, ...]}
    this.events = {};
  }
  on(eventName, callback) {
    if (this.events[eventName]) {
      this.events[eventName].push(callback);
    } else {
      this.events[eventName] = [callback];
    }
  }
  emit(eventName) {
    this.events[eventName].forEach((callback) => callback());
  }
}
  • 按顺序执行每个任务
// 任务集合,有三个任务
function* tasks(){
    yield 111; // 任务一,最终产生的结果为111
    yield 222; // 任务二,最终产生的结果为222
    yield 333; // 任务三,最终产生的结果为333
}
// 调用生成器函数,获取迭代器对象
let it = tasks()
// 自定义遍历迭代对象的方法
function next(){
    // 调用next方法获取一个任务的结果value,及是否所有任务执行完的标识done
    let {value, done} = it.next()
    if(done){
        console.log('所有任务执行完毕')
    }else{
        // 输出任务结果,继续下一个任务
        console.log(value)
        next()
    }
}
// 从第一个任务开始执行
next()
  • 有异步任务,同样希望按顺序执行,执行完上一个任务,才会执行下一个任务
// 任务集合,有三个任务(包含异步任务)
function* tasks() {
  yield 111;
  yield new Promise((resolve) => {
    setTimeout(() => {
      resolve(222);
    }, 2000);
  });
  yield 333;
}

let it = tasks();

function next() {
  let { value, done } = it.next();
  if (done) {
    console.log("所有任务执行完毕");
  } else if (typeof value.then === "function") {
    // 是一个promise任务,等待执行完异步任务后,再继续向下执行其他任务
    value.then((res) => {
      console.log(res);
      next();
    });
  } else {
    console.log(value);
    next();
  }
}
next();
  • 暂停、恢复任务的执行
<button id="btn">btn</button>
<script>
  btn.onclick = () => {
    pubSub.emit("send"); // 触发订阅事件的执行,会恢复总任务的继续执行
  };
  class PubSub {
    constructor() {
      this.events = {};
    }
    on(eventName, callback) {
      if (this.events[eventName]) {
        this.events[eventName].push(callback);
      } else {
        this.events[eventName] = [callback];
      }
    }
    emit(eventName) {
      this.events[eventName].forEach((callback) => callback());
    }
  }
  let pubSub = new PubSub();

  // 任务集合,有三个任务(包含异步任务)
  function* tasks() {
    yield 111;
    yield new Promise((resolve) => {
      setTimeout(() => {
        resolve(222);
      }, 2000);
    });
    yield 333;
    yield "pause"; // 遇到该任务(标识)时暂停任务的执行
    yield 444;
  }

  let it = tasks();

  function next() {
    let { value, done } = it.next();
    if (done) {
      console.log("所有任务执行完毕");
    } else if (value === "pause") {
      // 遇到pause标识时,先订阅一个事件,不继续往下执行任务
      pubSub.on("send", () => {
        console.log("send事件触发了");
        next(); // 当该订阅的事件被执行了,则继续往下执行任务
      });
    } else if (typeof value.then === "function") {
      // 是一个promise任务,等待执行完异步任务后,再继续向下执行其他任务
      value.then((res) => {
        console.log(res);
        next();
      });
    } else {
      console.log(value);
      next();
    }
  }
  next();
</script>
  • 嵌套的生成器函数,执行时,一层层进去,再一层层出来,最后执行完所有任务
function isCustomIterable(obj) {
  // 检查对象是否有[Symbol.iterator]方法,且不能是字符串
  if (typeof obj[Symbol.iterator] === "function" && typeof obj !== "string") {
    // 获取迭代器
    const iterator = obj[Symbol.iterator]();
    // 检查迭代器是否有next方法
    if (typeof iterator.next === "function") {
      // 排除内置的数组迭代器
      if (!Array.isArray(obj)) {
        return true;
      }
    }
  }
  return false;
}
function* task2() {
  yield "任务二开始";
  yield new Promise((resolve) => {
    setTimeout(() => {
      resolve("任务三:异步任务");
    }, 2000);
  });
  yield "任务二结束";
}
function* task1() {
  yield "任务一开始";
  yield task2(); // 任务二
  yield "任务一结束";
}
function* tasks() {
  yield "总任务开始";
  yield task1(); // 任务一
  yield "总任务结束";
}

// 入口函数,执行生成器函数(generator)的函数(run)
function run(generator, cb) {
  const it = typeof generator === "function" ? generator() : generator; // 执行生成器函数,获取迭代器对象
  // 自定义挨个执行任务的函数,作用就是挨个执行任务,每个任务就是一个生成器函数,每个生成器函数可以生成一个迭代器对象
  // 每执行一次run方法,就会产生一个myNext方法
  function myNext() {
    const { value, done } = it.next(); // 执行next方法获取一个任务的结果value,及是否所有任务都完成的标识done
    if (done) {
      console.log("当前任务所有步骤都完成");
      cb && cb(); // 每个生成器函数中有n个yield,那么需要执行n+1次next,该生成器函数才能执行完(即该函数才能执行完)
    } else if (isCustomIterable(value)) {
      // 自定义迭代器对象
      // console.log(`结果的数据类型,${typeof value},结果(自定义迭代器值):${value}`);
      run(value, myNext); // 递归调用,执行生成器函数
    } else if (value instanceof Promise && typeof value.then === "function") {
      value.then((res) => {
        console.log(res);
        myNext();
      });
    } else {
      // 非自定义迭代器对象
      // console.log(`结果的数据类型:${typeof value},结果(普通值):${value}`);
      console.log(value);
      myNext();
    }
  }
  myNext();
}

run(tasks);

再见来不及挥手

  • 迭代器对象具有可被遍历的特性
  • 生成器函数可产生迭代器对象
  • 生成器函数里面的代码可被控制执行
  • 生成器函数中有nyield,那么需要调用n+1next方法,生成器函数才会执行完
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值