Generator生成器 手写async函数

90 阅读4分钟

Generator生成器基本语法

/*--创建一个生成器--*/
function * generator(){
    console.log('ok');
}
//generator()并不会把函数执行 而是返回个generator实例 也可以说 生成器执行返回迭代器
let g = generator();
console.log(g);
console.log(g instanceof generator) //=>true
/*
	g的原型链上拥有
	+ next
	+ return
	+ throw
	+ Symbol.toStringTag : 'Generator'
*/

next使用

/*
	g.next()返回一个对象里面包含value,done
	yield 后面的 作为value中的值
*/
function * generator(){
   console.log('A');
   yield 1;

   console.log('B');
   yield 2;

   console.log('C');
   yield 3;

   console.log('D');
   return 4
}

let g = generator();
console.log(g.next()); //=>{value:1,done:false}
console.log(g.next()); //=>{value:2,done:false}
console.log(g.next()); //=>{value:3,done:false}
console.log(g.next()); //=>{value:4,done:true} 
console.log(g.next()); //=>{value:undefined,done:true}
/*---*next传参---*/

function * generator(){
   console.log('A');
    //如果第二个next什么都没传a就是undefined
   let a= yield 1;

   console.log('B',a);	//=>B 要
   let b= yield 2;

   console.log('C',b);	//=>C 学
   let c= yield 3;

   console.log('D',c);	//=>C 习
   return 4
}

let g = generator();
console.log(g.next('我')); 
console.log(g.next('要'));
console.log(g.next('学'));
console.log(g.next('习')); 

/*
	@1 走到yield 那一行就会停止
	@2 next('参数是给上一个yield赋值不传就是undefined')
*/

生成器函数嵌套生成器函数

function * generator1 (){
    yield 1;
    yield 2;
    yield generator2();
}

function * generator2 (){
    yield 4;
    yield 5;
    yield 6;
}

let g = generator1();
console.log(g.next());	//=>{value:1,done:false}
console.log(g.next());	//=>{value:2,done:false}
console.log(g.next());	//=>{value:generator2,done:true}

/*------------*/

function * generator1 (){
    yield 1;
    yield 2;
    // '*' + 'generator2()返回的迭代器'
    yield * generator2();
}

function * generator2 (){
    yield 4;
    yield 5;
    yield 6;
}

let g = generator1();
console.log(g.next());	//=>{value:1,done:false}
console.log(g.next());	//=>{value:2,done:false}
console.log(g.next());	//=>{value:4,done:false}
console.log(g.next());	//=>{value:5,done:false}
console.log(g.next());	//=>{value:6,done:false}
console.log(g.next());	//=>{value:undefined,done:true}

发送串行请求

方法一 :基于Promise中的then链实现即可

return new Promise((resolve,reject) => {
        setTimeout(() => {
            resolve(wait);
        },wait)
    })
}

query(1000).then((res) => {
    console.log("第一次执行",res);	//一秒后输出 1000
    //如果不返回一个状态不确定的promise 下面那个then就会立即执行
    return query(2000);
}).then((res) => {
    console.log("第二次执行",res);	//再过两秒后输出 2000
})

方法二 :generator生成实例手动一次次调用next

/*
	思路:
	 	@1 运用next一步一步执行
	 		第一次 g.next() => let result = yield query(1000); 执行后返回一个promise在value中状态为pedding,等一秒后定时器到了状态变为fulfilled开始执行then
	 		第二次 g.next(res) => @1 console.log(result); @2result = yield query(2000); 第二次是在第一次状态变为fulfilled开始执行then的时候触发的 输出第一次的结果  g.next(res)中需要传res给上一次的yield
	 		.....
	 		最后一次  g.next(res) => console.log(result);输出 返回 {value:undefined,done:true}
*/
const query = wait => {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve(wait);
        }, wait)
    })
}

function* generator() {
    let result = yield query(1000);
    console.log(result);

    result = yield query(2000);
    console.log(result);

    result = yield query(3000);
    console.log(result);
}

let g = generator();
//g.next().value 这是一个promise 等他定时器到了一秒我们就进入then
g.next().value.then((res) => {
    g.next(res).value.then((res) => {
        g.next(res).value.then((res) => {
            g.next(res);
        })
    })
})

上面产生的回调地狱十分恶心 我们可以写一个函数自动帮我们执行

generator + promise

// 判断一个参数是不是promise
const isPromise = function isPromise(obj) {
    if (obj !== 'null' && /(function|object)/i.test(typeof (obj))) {
        if (typeof (obj.then) === 'function') {
            return true
        }
    }
    return false;
}
/*
    执行query 传一个参数 返回值是一个promise 里面有个定时器 定时器时间到了把promise的状态改变
*/
const query = wait => {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve(wait);
        }, wait)
    })
}

function* generator() {
    let result = yield query(1000);

    console.log(result);
    result = yield 1;
    //result = yield Promise.reject('NONO');

    console.log(result);

    result = yield query(3000);
    console.log(result);
}

//创建一个AsyncFunction函数 形参:generator生成器函数 
const AsyncFunction = function AsyncFunction(generator, ...args) {
    //返回一个promise对象
    return new Promise((resolve, reject) => {
        //执行生成器函数generator 返回一个迭代器 Iterator 缩写itor
        let itor = generator();
        //定义一个next方法 用来之后的递归 接受一个参数x
        const next = x => {
            //@1 第一次进来x是undefined   itor.next(x) => let result = yield query(1000) 返回{value:promise,done:false}
            let { value, done } = itor.next(x);
            //如果itor.next(x)执行返回的done为true就说明generator执行完了 
            if (done) {
                resolve('OK');	//直接返回
                return;		//递归结束
            }
            //判断一下value是不是promise 如果不是就转换成promise 确保value是一个promise
            if(!isPromise(value)) value = Promise.resolve(value);
            value.then((res) => {
                //第一次定时器结束 promise状态确定 就递归调用
                /*
                    分析一下res
                        + 第一次res是1000,是resolve(wait);中wait传过来的
                        +  next(res);不写就不能给x传参itor.next(x)=> console.log(result);
   						   result = yield query(2000); 中的result就是undefined 应为没有给上一次yield传值
                */
                next(res);
            }, (err) => {
                /*
                    为什么只能拿到 yield query(2000)中的错误
                        value拿到的promise是yield返回的
                */
                reject(err);
            })
        }
        //第一次执行不传参数
        next();
    })
}

AsyncFunction(generator).then((res) => {
    console.log(res);
}).catch(err => {
    console.log(err);
});

处理异步的终极方案

async await

原理就是 generator + promise

const query = wait => {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve(wait);
        }, wait)
    })
}

async function generator() {
    let result = await query(1000);
    console.log(result);

    result = await query(2000);
    console.log(result);

    result = await query(3000);
    console.log(result);
}

generator()