重学前端— Iterator promise async generator

迭代器Iterator

迭代器 是一个对象 也可称为可迭代对象,提供一个next方法 去获取迭代器中下一项,next方法是一个函数返回一个对象{done:boolean,value:any}

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

var iterator = makeIterator([10,20,30])
console.log(iterator.next()) // {value: 10, done: false}
console.log(iterator.next())// {value: 20, done: false}
console.log(iterator.next())// {value: 30, done: false}
console.log(iterator.next()) // {done: true}

可迭代对象

可迭代对象内部实现了一个@@Iterator方法,就是数据结构或者原型链上有一个Symbol.iterator属性(遍历器生产函数),它返回一个遍历器对象。只要一个数据结构有S ymbol.iterator就可以认为是可迭代的。

const obj = {
 a:1
}

for (const i of obj) {  //  报错: obj is not iterable
console.log(i)
}

// 手动给obj加Symbol.iterator属性 它就变为可迭代对象
obj[Symbol.iterator] = function() {return makeIterator([1,2,3]) }

// 可以使用 for of 进行访问
for (const i of obj) {
console.log(i)  // 1  2 3
}

// 可以被Arraty.form 转为数组
console.log(Array.from(obj)) // [1,2,3]  

// for in 是可遍历
console.log(Object.getOwnPropertyDescriptors(obj))
/* {
  a: { value: 1, writable: true, enumerable: true, configurable: true },
 [Symbol(Symbol.iterator)]: {
    value: [Function (anonymous)],
    writable: true,
    enumerable: true,
    configurable: true
  }
} */

for (const i in obj) {
  console.log(i) // a
}

Object.defineProperty(obj,'b', { value: 2, writable: true, enumerable: true, configurable: true })

for (const i in obj) {
console.log('test: '+i) // a b
}

Object.defineProperty(obj,'b', { value: 2, writable: true, enumerable: false, configurable: true })

for (const i in obj) {
console.log(i) // a
}

*特性:

  1. 具Symbol.iterator属性
  2. 可以使用 for of 进行访问
  3. 可以被Arraty.form 转为数组

这里有个常见误区,我们老说的可以for in遍历其实是枚举,就是数据属性的enumrable 为true 可以被for in 访问元素

而 可迭代(可遍历),是有Symbol.iterator 属性,可以被 for of 访问原型

对象可以用for in 不能 用for of ,其实就是因为这个,可以说对象可以枚举,但是不可迭代

原生的可迭代对象

  • 数组
  • SET
  • MAP
  • 类数组 arguments DOM NODELIST
  • GENERATOR对象
  • 字符串

手动实现迭代器

就是调用数据的Symbol.iterator属性 获得迭代器构造函数 调用next

function Forof(data,cb) {
 if(typeof data[Symbol.iterator] !== "function") {
   throw new TypeError(' is not iterable');
 }

 if(typeof cb !== "function") {
  throw new TypeError('cb is not function');
 }


 const iterator = data[Symbol.iterator]();

 let result = iterator.next();
 while(!result.done) {
   cb(result.value);
   result = iterator.next();
 }
}

Forof([1,2],console.log) // 1,2

内置迭代器

  1. Object.keys
  2. Object.values
  3. Object.entries

异步promise

回调地狱

Promise 是为了解决回调地狱的问题,回调地狱是一个请求成功后再执行另一个请求再执行 如此嵌套 导致代码看起来比较复杂,不易维护

定义

Promise 是一个对象 里面保存着未来才会结束的事件,比如异步操作。

特点:

  1. 3种状态 pending rejected fulfilled 只由异步操作决定当时是那种状态,其他操作都不能改变这个状态,不受外界影响
  2. 状态一旦改变,就不会再变。一开始是pending, 只能从pending 变为fulfilled 或者 rejected ,没有其他情况。

APi

  1. 创建实例 new Promise 成功resolve 失败reject. 调用这个实力的then方法,传递两个参数分别是 成功和失败的回调函数
const promise = new Promise((reslove,reject) {
 if(/* 异步成功*/) {
  resolve(value)
 } else {
  reject(error)
 }

})

promise.then(funtion(value) {
  // success
},function(error){
  // failed
}).catch(error => {
  // 发生错误的回调
})
.finally(() => {
  // 不管最后啥状态 一定会执行
});
  1. p = Promise.all(a1,a2,a3…) 将多个p promise 包装成一个 参数是p romise实例或者可迭代对象。只有当a1,a2,a3 全部变为fulfilled 整体p才会fulfilled;只要其中一个rejected p就会rejected,会回传第一个rejected的值给回调函数
  2. p=Promise.race(a1,a2,a2…) 同上也是包装多个promise, 只要其中一个率先改变状态,就会把这个返回值传递给回调
  3. p = Promise.allSettled(a1,a2,a3…)同上也是包装多个promise, 当所有的实例的状态都确定时,会把所有的值都传递给回调函数
  4. p = Promise.resolve(…)将现有对象转为p romose对象,如果参数时promise 就原样返回,如果是可迭代对象(具有t hen方法的对象)会把这个对象转为promise 并且立即调用then方法;如果参数时原始值,就返回一个新的promise 状态时fulfilled;不带参数,返回fulfilled状态的promise对象
  5. p = Promise.reject() 返回一个新的 Promise 实例,该实例的状态为rejected

实例

  1. 红绿灯:3s red 3s green 1s yellow

    // 回调地狱写法
    const stepCB = () => {
      setTimeout(()=> {
        console.log('red');
         setTimeout(()=> {
           console.log('green')
            setTimeout(()=> {
              console.log('yellow')
              stepCB();
            },1000)
         },2000)
      },3000)
    }
    
    // promise写法
    const promiseGRBL = (time,color) => {
      return new Promise((reslove,reject)=> {
        setTimeout(()=> {
          console.log(color);
          resolve()
        },time)
      })
    }
    
    const step = () => {
      promiseRGBL(3000,'red').then(()=> {
        return promiseGRBL(2000,'green');
      }).then(()=> {
        return promiseGRBL(1000,'yellow');
      }).then(()=> {
        step();
      })
    }
    
    
  2. 把xmlHttpRequest 封装成同步写法

    function request(config) {
      const {method,url,resposeType,data}  = config;
      return new Promise((resolve,reject) => {
        const xhr = new XmlHttpRequest();
        xhr.open(method,url);
        xhr.onreadystatechange = function() {
          if(this.readyState !== 4) {
            return 
          }
          if(this.status === 200) {
            resolve(this.response)
          } else {
            reject(new Error(this.stateText))
          }
        }
        xhr.responsType = responseType;
        xhr.setRequsetHeader('Accept',"application/json");
        data ? xhr.send(data) : xhr.send()
      })
    }
    
    requst({method:'get',url:'xxx',responseTyepe:'json',data:{id:1}})
    

手写 promise

  1. 用构造函数 创建一个对象 对象具有state 状态默认值是pending, result 存储成功结果,reason存储失败原因,onResolves 存在异步成功回调,onRejects 存储异步失败回调,构造函数有一个promise构造器函数的参数,在构造函数里面调用构造器,传入两个参数,一个resolve函数, 一个reject函数。在构造函数内部创建一个resolve函数判断状态是pending时就把状态改为fuilfilled,把resolve的结果赋值给result,同上遍历执行onResolves里的的函数;同理reject函数也是如此,只是存的失败的reason 状态是rejected 遍历执行的是onRejects函数。
  2. 构造函数的原型链上定义一个then方法,传入两个参数,一个成功回调函数onResolve,一个失败的回调函数onReject,判断这两个类型,如果不是函数就赋值为参数是value 返回值是value的函数,。为了能链式调用,then方法返回一个新的promise对象。在这个对象里头,判断state是pending的时候表示是异步,就把回调函数存起来。state是fulfilled的时候 就执行onResolve函数,并把结果传给一个resolvePromise函数,同理当state是rejected的时候也一样,就是执行onReject函数。
  3. resolvePromise函数 传入3个参数 回调的返回值 resolve reject函数,判断回调返回值的类型是不是对象或者函数,如果是,就判断它的then类型,如果是函数就运行这个函数,把回调值传入当this reolve reject 传入当参数,如果不是 就resolve这个值
function MyPromise(fn) {
   this.state = 'pending';
   this.result = null;
   this.reason = null;
   this.onResolves = [];
   this.onRejects = [];
   const resolve =  (result) => {
     if(this.state === 'pending') {
       this.state = 'fulfilled'
        this.result = result;
        this.onResolves.forEach(function(fn) {fn()})
     }
   }
   const reject =  (reason) => {
     if(this.state === 'pending') {
       this.state = 'rejected'
        this.reason = reason;
        this.onRejects.forEach(function(fn) {fn()})
     }
   }
   try { fn(resolve,reject)} catch(e) {reject(e)}
}



const promoseResolve = (value,resolve,reject) => {
  
  if(typeof value !== null &&(typeof value === 'function' || typeof value === 'object')) {
    const then = x.then;
    if(typeof then === 'function') {
      try{
        then.call(value,resolve,reject)
      } catch(e) {
        reject(e)
      }
    } else {
      resolve(value)
    } 
  } else {
      resolve(value)
  }
}

MyPromise.prototype.then = function(onResolve,onReject) {
  // 根据规范,如果then的参数不是function,则我们需要忽略它, 让链式调用继续往下执行
  if(typeof onResolve !=== 'function') {
    onResolve = (result)=> result
  }
   if(typeof onReject !=== 'function') {
    onReject = (reason)=> reason
  }
  
  const promise = new MyPromise((resolve,reject)=>{
    
    const onRejectWrap = ()=> {
      // 这里加setTimeout 是因为promise并不是一成功就执行回调 而是放在任务队列中,这里是放在宏任务中
       setTimeout(()=> {
         const reason = onReject();
         promoseResolve(reason,resolve,reject)
       },0)
    }
    
     const onResolveWrap = ()=> {
       setTimeout(()=> {
         const result = onResolve()
         promoseResolve(result,resolve,reject)
       },0)
    }
    
     if(this.state === 'pending') {
    	this.onResolves.push(onResolveWrap);
    	this.onRejects.push(onRejectWrap);
  	}
  
    if(this.state === 'fulfilled') {
      onResolveWrap()
    }

   if(this.state === 'onReject') {
    	onRejectWrap()
  	}
  })
  
  return promise;
  
}

// 失败执行回调
MyPromise.prototype.catch = function(fn) {
   this.then(null,fn)
}
// MyPromise.resolve执行回调,并在then中return结果传递给后面的Promise
PromiseA.prototype.finally = function (fn) {
    this.then(value => PromiseA.resolve(fn()).then(() => value),
        reason => PromiseA.resolve(fn()).then(() => {
            throw reason
        })
    )
}

// 如果类型是primise就直接返回 如果不是就包装成一个resolve promise 
MyPromise.resolve = function(data) {
  if(data instanceOf MyPromise) {
    return data
  }
  return new MyPromise(resolve => {
    resolve(data)
  })
}
// 包装成一个reject的promise
MyPromise.reject = function(data) {
  return new MyPromise((_,reject) => {
    reject(data)
  })
}

// 返回一个promise 遍历所有的传入的数组 如果是promise就直接then 不是就是resolve包装成promise 再 then, then成功回调里头 计数 如果都成功就resolve 成功返回数组,如果不成功 就直接reject reason
MyPromise.all = function(list) {
  return new MyPromise((resolve,reject) => {
  let count = 0;
  const len = list.length;
  const result = []
  for(let i =0;i<len;i++) {
    MyPromise.resolve(list[i]).then(v=> {
      result[i] = v
      if(++count === len ) {
        resolve(result)
      }
    },r=> {
      reject(r)
    })
  }
  })
  
}

// 返回一个promise 遍历传入的数组 如果是promise直接then  如果不是就先resolve包装为promise再then, then里面,如果成功就直接成功 返回结果,失败就直接失败返回原因
MyPromise.race = function(list) {
  return new MyPromise((resolve,reject) => {
    const len = list.length;
     for(let i =0;i<len;i++) {
       MyPromise.resolve(list[i]).then(v=> {
         resolve(v)
       },r=> {
         resolve(r)
       })
     }
  })
 
  // 返回一个promise 遍历传入的数组 如果是promise直接then  如果不是就先resolve包装为promise再then, then里面,成功就把成功及结果存起来 装饰fulfilled 失败就把失败原因存起来,状态是rejected 。当所有数据都执行完时 就 resolve 所有结果
 MyPromise.allSetteld = function(list) {
  return new MyPromise((resolve,reject) => {
    let count = 0;
    const len = list.length;
    const result = []
     for(let i =0;i<len;i++) {
       MyPromise.resolve(list[i]).then(v=> {
         result.push({state:'fulfilled',value:v})
         if(++count === len ) {
           resolve(result)
         }
       },r=> {
          result.push({state:'rejected',reason:r})
         if(++count === len ) {
           resolve(result)
          }
       })
     }
  })
}


异步generator 迭代器生成器

Generator 是一个普通函数,function 和函数名直接有个*号,函数里面用关键字yield 定义内部状态; 调用这个函数不返回结果,返回的是迭代器对象,有一个then方法,返回{value:值,done: true or false },then 方法 调用遇到yield 暂停 返回yield定义的状态,再then 继续执行,直到执行完,done变为false

function* generator() {
  yield 1;
  yield 2;
  yield 3
}

const gen = generator()
gen.then() // {value:1,done:false}
gen.then()// {value:2,done:false}
gen.then()// {value:3,done:false}
gen.then()// {value:undefined,done:true}


console.log(g[Symbol.iterator]() === g) // true  generator 调用返回的是迭代器对象 它的Symbo.iteratro 属性 也是一个遍历器生成函数 调用返回自己

// 用递归遍历
const next = () => {
    let data = g1.next()
    console.log(data)
    if(data.done) {
        return
    }
    next()
}
next()

generator生成器实现方式:核心是有个context去保存上下文,前一个后一个值,以及done状态,每次执行时都会判断从哪里开始执行。

function gen$(_context) {
    while (true) {
        switch (_context.pre = _context.next) {
            case 0:
                _context.next = 2;
                return 'result1';
            case 2:
                _context.next = 4;
                return 'result2';
            case 4:
                _context.next = 6;
                return 'result2';
            case 6:
            case 'end':
                return context.stop()
        }
    }
}

const context = {
    pre :0,
    next:0,
    done: false,
    stop:function (){
        this.done = true
    }
}

let gen = function () {
    return {
        next: function () {
            return {value: context.done ? undefined: gen$(context),done: context.done}
        }
    }
}

var g = gen();
console.log(g.next()) // { value: 'result1', done: false }
console.log(g.next()) // { value: 'result2', done: false }
console.log(g.next()) // { value: 'result2', done: false }
console.log(g.next()) // { value: undefined, done: true }

异步async await

  • Async await 是 genetator的语法糖

  • 执行async函数 返回的一定是promise对象

  • await能够返回Promise的resolve/reject的值 相当于then

Async await的实现:就是基于generator的封装,传入genarator 执行遍历器,返回promise ;promise 里面执行 递归调用next,这里为了兼容primise和普通值 用promise resolve包装统一为promise, 当遍历结束时 resolve value值

function run(gen) {
    return new Promise((resolve,reject) => {
        const g= gen();
        function next(value) {
            let result = null
            try {
                result = g.next(value);
            } catch (e) {
                return reject(e)
            }
            if(result.done) {
                return resolve(result.value)
            }
           Promise.resolve(result.value).then(v=> next(v),err=> g.throw(err))
        }
        next()
    })
}

function* genTest (){
    const r1 = yield Promise.resolve(1);
    const r2 = yield Promise.resolve(r1+1);
    const r3 = yield new Promise(resolve=> setTimeout(()=> resolve(r2+1),1000));
    const r4 = yield r3
    return r4
}

// 调用
run(genTest).then(v=> {
    console.log(v) // 3
})
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值