【魔仙带你拿下Iterator迭代器/generator生成器/async/await语法糖🍬】

139 阅读2分钟

Iterator迭代器

iterator 一种接口/规范 任何数据结构有了它 就拥有可以按顺序遍历或是访问里面值的魔法🪄🧙‍♀️

iterator语法
const obj = {
   [Symbol.iterator]: function(){}
}
iterator迭代器创建了一个指针 最初指向第一条数据 每次调用next()方法指针会不断往后移动
每一次next会返回一个对象 这个对象包含两个 value 和 done
value:当前数据的值
done:表示遍历是否结束 布尔值

对象没有部署该接口 需要手动实现
1.因为对象遍历顺序不好确定
2.map数据结构弥补了它的缺陷 map有iterator接口 所以对对象部署没有必要

手写对象部署iterator
let obj = {...}
obj[Symbol.iterator] = function () {
  let keyList = Object.keys(obj),
      index = 0;
   return {
     next(){
       return index < keyList.length ?
        { value:{
           key: keyList[index],
           val: obj[keyList[index++]]
        } 
        }:{done:true}
     }
   }
}
for(let key of obj){console.log(key)}

generator生成器

生成器的语法是在function和函数名之间有一个* 与yield搭配使用 可以暂缓代码哦😯

ES6新增的语法 用于实现异步编程 是iterator的具体实现方式 可以😌掌控函数的执行

写法:function* foo(){} function *foo(){}
yield只能出现在生成器函数体内

案例

function *foo(x) {
  let y = 2 * (yield (x + 1))
  let z = yield (y / 3)
  return (x + y + z)
}
let it = foo(5)
console.log(it.next())   // => {value: 6, done: false}
console.log(it.next(12)) // => {value: 8, done: false}
console.log(it.next(13)) // => {value: 42, done: true}

解读:
1.第一次执行next 会忽略入参 第一个yield抛出表达式的值5+1=6 暂停
2.第二次执行next 入参等于上一个表达式的值 
所以此时y = 2 * 12 = 24 第二个yield暂停 抛出表达式 y/3 = 24/3 = 8 
3.第三次执行next 入参等于上一步表达式的值 所以这里z = 13 函数返回x+y+z= 5+24+13=42

async await语法

是生成器的语法糖🍬 更好的语义/适用性 返回值是promise 这个promise下一章总结

await 优点在于处理then的调用链 解决了回调地狱
缺点 await把异步改造成了同步 如果滥用 多个异步代码无依赖性会导致性能上的下降
async => *
await => yield

原理 使用生成器+自动执行器
手写async

function getNum(num){ //promise 模拟异步请求 作用是入参++
   return new Promise((resolve,reject) => {
     setTimeout(() => {
      resolve(num+1)
     },1000)
   })
}

function asyncFn(fn){ //自动执行器
  var gen = fn();
  
  function next(data){
   var res = gen.next(data)
   if(res.done) return res.value
   res.value.then( data => { //没有执行完 则递归
     next(data) 
   })
  }
  next()
}

var fn = function* (){ 生成器函数
var a = yield getNum(1);
var b = yield getNum(a);
console.log(b)
}
asyncFn(fn)