ES6-20【iterator与generator】

ES6-20【iterator与generator】,第1张

一、iterator

迭代器的实现

function makeIterator(arr){
        var iterRatorIndex = 0;
        return{
            next(){
              return  arr.length > iterRatorIndex ? 
               {value:arr[iterRatorIndex++],done:false}:
               {value:undefined,done:true}
            }
        }
    }
var test = makeIterator(['a','b']); //闭包:这里返回对象里面的函数,绑定了外部的作用域
console.log(test.next());//{value: "a", done: false}
console.log(test.next());//{value: "b", done: false}
console.log(test.next());//{value: undefined, done: true}

迭代器与迭代器模式

迭代器:能够迭代有序的,连续的结构,每一次抽取一位消耗数据的形式

迭代器模式:结构化的模式,迭代器是结构化模式的实现方式,从源以一次一个的方式抽取

抽取就是每次只遍历一个值

内部迭代器和外部迭代器

前端不分内外迭代器

内部迭代器:系统内部定义好的迭代规则,在进行调用的时候就可以拿到所有遍历的元素

外部迭代器:自己部署定义一个迭代器接口,每一次抽取一个数据、每一次迭代一个数据

部署迭代器接口——next方法(迭代对象)

对象并不具备迭代器接口(因为对象是无序的所以不能抽取),要想迭代就得部署一个迭代器接口

方法一

let obj = {
    start:[1,3,2],
    end:[7,8,9],
    [Symbol.iterator](){
        let arr = [...this.start,...this.end];
        let iterRatorIndex = 0;
        return{
        next(){
          return  arr.length > iterRatorIndex ? 
           {value:arr[iterRatorIndex++],done:false}:
           {value:undefined,done:true}
        }
    }
    }
}
for(let a of obj){
	console.log(a);  
}// 1 3 2 7 8 9

方法二

map结构则是有序的,让map模拟对象进行迭代,以键值的方式进行输出

可迭代的数据类型:array/map/set/string/typeArray/NodeList/arguments

let obj = {
    a:1,
    b:2,
    [Symbol.iterator]() {
        let map = new Map();
        for (let [key, value] of Object.entries(obj)) {
          map.set(key, value);
        }
        let mapArr = [...map.entries()];
        
        let iterRatorIndex = 0;
        return {
            next() {
                return mapArr.length > iterRatorIndex ?
                    { value: mapArr[iterRatorIndex++],done:false}:
           {value:undefined,done:true}

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

//抽取
var a = obj[Symbol.iterator]();
console.log(a.next());
//{value: Array(2), done: false}  

用for(let [key,value] of Object.entries(obj))也能解决这个问题,但是不能实现抽取,因为不具备迭代器接口

默认会调用iterator的情况

…拓展运算符for…ofArray.from()map/setPromise.all([…])/Promise.race([…])yield

部署迭代器接口——return方法(了解)

终止for循环的方法都会触发return方法(比如break/throw new Error())

let obj = {
    a:1,
    b:2,
    [Symbol.iterator]() {
        let map = new Map();
        for (let [key, value] of Object.entries(obj)) {
          map.set(key, value);
        }
        let mapArr = [...map.entries()];
        
        
        let iterRatorIndex = 0;
        return {
            next() {
                return mapArr.length > iterRatorIndex ?
                    { value: mapArr[iterRatorIndex++],done:false}:
           {value:undefined,done:true}

        },
        return() {
            console.log('触发了错误');
            return {value: 1, done: false} // 不返回该对象会报错
        }
        }
    }
}
for(var i of obj){
    console.log(i);
    throw new Error('错误');
}
//["a", 1]
//触发了错误
//Uncaught Error: 错误
二、generator

概念

生成器函数,返回值是迭代器实例

基本形式

function * test(){}
let iter = test();
console.log(iter);
test {<suspended>}
//[[GeneratorLocation]]: VM657:1
//[[Prototype]]: Generator
//[[Prototype]]: Generator
//constructor: GeneratorFunction {prototype: Generator, Symbol(Symbol.toStringTag): 'GeneratorFunction', constructor: ƒ}
//next: ƒ next()  <<
//return: ƒ return()
//throw: ƒ throw()
//Symbol(Symbol.toStringTag): "Generator"
...

yield

特点

产出迭代器对象,暂停函数运行

返回值默认是undefined,如果要有返回值则需通过next传参

function * test(){
    console.log(1);   
    yield 'a';
    console.log(2);
    yield 'b';
    console.log(3); 
    let a = yield '5'
    console.log(a);
    return 'd';
    yield 'c';
   
}
let iter = test();
console.log(iter.next());
console.log(iter.next());
console.log(iter.next());
console.log(iter.next());
console.log(iter.next());
 
// 1
// {value: "a", done: false}
// 2
// {value: "b", done: false}
// 3
// undefined
// {value: "d", done: true}
// {value: undefined, done: true}

yield是一个单独的表达式,嵌套使用要加()

function * test(){
    console.log('hello'+(yield 123)); 
   
}
let iter = test();
console.log(iter.next());
{value: 123, done: false}

yield作为函数形参

function * test(){
    foo(yield 'a',yield 'c')
   
}
function foo(a,b){
    console.log(a,b);
    
}
let iter = test();
console.log(iter.next());
console.log(iter.next());
console.log(iter.next());
// {value: "a", done: false}
// {value: "c", done: false}
// undefined undefined
// {value: undefined, done: true}

yield/return区别:

yield 暂停(有记忆的功能)

return 结束(没有记忆功能)

注: yield 只能出现在生成器函数中,否则会报错

for…of 遍历产出值

function * test(){
    yield 1;
    yield 2;
    yield 3;
    yield 4;
    yield 5;
   
}
for(let i of test()){
    console.log(i);
    
}
//1 2 3 4 5

通过next给yield传参

永远拿不到第一次的值,所以第一个参数没必要传,蛇形传值

function * test(){
    
    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 iter = test();
console.log(iter.next('none'));
console.log(iter.next('two'));
console.log(iter.next('three'));
// {value: 1, done: false}
// value1two
// {value: 2, done: false}
// value2three
// {value: 3, done: false}

迭代器接口优化

因为yield得产出值就为迭代函数,所以可以直接用yield去替代以前next的写法

let obj = {
    start:[1,2,3],
    end:[4,5,6],
    [Symbol.iterator] : function*(){
        var itemIndx = [...this.start,...this.end],
            length = itemIndx.length,
            idxof = 0;
            
         while(length>idxof){
             yield itemIndx[idxof++]
         }   
        
    }
}
for(var c of obj){
    console.log(c);
    
}
console.log(obj[Symbol.iterator]().next());
// 1 2 3 4 5 6 {value: 1, done: false}

迭代器方法

next() —— 抽取产出值,返回值是迭代器对象

return() —— 终止迭代,往后迭代都是 {value:undefine,done:true}

throw() —— 抛出错误,由于yield会暂停,导致try-catch没执行完,所以捕获不到,只有try-catch执行完了才能捕获到错误(相当于next,也可以抽取迭代)

var g = function * (){
		yield;
	try{
		yield;
	}catch(e){
		console.log('生成器内部异常:' + e);
	}
	console.log(1);
}
var i = g();
console.log(i.next()); // {value:undefined,done:false} --没执行完
console.log(i.next()); // {value:undefined,done:false} --执行完了
console.log(i.throw('a')); // 生成器内部异常:a --抛出错误   注意:这里只能throw一次,两次会报错
console.log(i.next()); // {value:undefined,done:true}

cry/catch可以捕获生成函数的异步错误

let fs = require('fs');
let util = require('util');
let co = require('co');
let readFile = util.promisify(fs.readFile);
function * read(){
	let value1 = yield readFile('./name.txt', 'utf-8');
	let value2 = yield readFile(value1, 'utf-8');
	let value3 = yield readFile(value2, 'utf-8');
	return value3;
}
let promise = co(read());
promise.then((val)=>{
	console.log(val);
});
// 99

generator状态机

generator状态机

let fs = require('fs')
function promisify(fn){
    return function(...args){
        return new Promise((resolve,reject)=>{
            fn(...args,(err,data)=>{
                if(data){
                    resolve(data)
                }else{
                    reject(err)
                }
            })
        })
    }
}    
let readFile = promisify(fs.readFile)
function * read(){
    let value1 = yield readFile('./name.txt','utf-8');
    let value2 = yield readFile(value1,'utf-8');
    let value3 = yield readFile(value2,'utf-8');
    console.log(value3);
}
let iter = read();
let {value,done} = iter.next();
value.then((val)=>{
   let{value,done} = iter.next(val)
    value.then((val2)=>{
        let{value,done} = iter.next(val2)
        value.then((value)=>{
            console.log(value);
        })
    })
})
// 99

步骤:

首先通过Promisify把异步函数promise化

然后把所有异步函数作为产出值放入生成函数,通过next进行抽取调用,返回值是迭代器对象{value:promise,done:false}

再通过模式匹配拿到promise,通过then拿到数据,继续通过next进行抽取调用

co源码实现

co是一个模块,是由TJ开发(整个git最高效、高产的一个人)

function Co(iter){
    return new Promise((resolve,reject)=>{
        let next = (data) =>{
            let {value,done} = iter.next(data);
            if(done){
                resolve(value)
            }else{
                value.then((val)=>{
                    next(val);
                })
            }
        }
        next(); // 递归
    })
}
let promise = Co(read())
promise.then((val)=>{
    console.log(val);
})
// 99

co

它让异步的执行更加简单,不需要每次都调用next

它也是async的由来

npm i co -D
let co = require('co');
let promise = Co(read())
promise.then((val)=>{
    console.log(val);
});
// 99

欢迎分享,转载请注明来源:内存溢出

原文地址:https://54852.com/web/941681.html

(0)
打赏 微信扫一扫微信扫一扫 支付宝扫一扫支付宝扫一扫
上一篇 2022-05-17
下一篇2022-05-17

发表评论

登录后才能评论

评论列表(0条)

    保存