Generator生成器函数的使用以及使用生成器实现async...await的原理

本文主要讲解生成器,需要先了解迭代器Iterator,对迭代器不了解的可以去看下对象如何部署迭代器的那篇文章,对迭代器有个初步的认识,部署了迭代器,就可以使用 for ... of 遍历对象啦,棒棒哒!

Generator生成器函数的作用:

可以基于返回的iterator (迭代器对象) ,基于其next方法,控制函数体中的代码,一步步的去执行!!+ 每一次执行next,控制函数体中的代码开始执行,直到遇到 yield 则等待!

done: false

value:yield后面的值

当遇到函数体中的return,或者已经执行到函数最未尾的位置了

done:true

value:函数的返回值或者undefined

「把它当做一个实例 __proto__」

普通函数是 Function 的实例,普通函数.__proto__===Function.prototype

生成器函数是 GeneratorFunction 的实例

生成器函数.__proto__===GeneratorFunction.prototype

GeneratorFunction.prototype.__proto__===Function.prototype

({}).toString.call(生成器函数) => "[object GeneratorFunction]"

「把它作为一个构造函数 prototype」

生成器函数不能被new执行 Uncaught TypeError: func is not a constructor

当做普通函数执行,返回的结果就是生成器函数的一个实例

itor.__proto__ -> func.prototype「空对象,没有constructor」 -> Generator.prototype「constructor:GeneratorFunction」{next/return/throw/Symbol(Symbol.toStringTag): "Generator"} -> 一个具备迭代器规范的对象「Symbol(Symbol.iterator)」 -> Object.prototype

声明generator函数:function后面加一个*

function* fn() {
console.log(this);
return 10;
}
fn.prototype.query = function () {};
let gen = fn();
console.log(gen);
// gen.__proto__ -> fn.prototype「query」 -> GeneratorFunction.prototype「next/return/throw/Symbol.toStringTag」-> xxx.prototype「Symbol.iterator」 -> Object.prototype
console.log(typeof fn); //->"function"
console.log(fn instanceof Function); //->true

function* generator() {
console.log('A');
yield 10;
console.log('B');
yield 20;
console.log('C');
yield 30;
console.log('D');
return 100;
}
let itor = generator();
console.log(itor.next()); //->{value:10,done:false}
console.log(itor.next()); //->{value:20,done:false}
console.log(itor.next()); //->{value:30,done:false}
console.log(itor.next()); //->{value:100,done:true}
console.log(itor.next()); //->{value:undefined,done:true}
function* generator() {
console.log('A');
yield 10;
console.log('B');
yield 20;
console.log('C');
return 30;
}
let itor = generator();
console.log(itor.next()); //->{value:10,done:false}
console.log(itor.return('@return')); //->{value:"@return",done:true}
console.log(itor.throw('@throw')); // 手动抛出异常,生成器函数中的代码都不会在执行了
console.log(itor.next()); //->{value:undefined,done:true}

// 每一次执行next的传递的值,是作为上一次yeild的返回值处理的
itor.next(N): 每一次执行next方法,传递的值会作为上一个yield的返回值所以第一次执行next方法,传递的值是没有用的,因为在它之前没有yield
function* generator() {
let x1 = yield 10;
console.log(x1);  // @2
let x2 = yield 20;
console.log(x2);   // @3
return 30;
}
let itor = generator();
itor.next('@1');
itor.next('@2');
itor.next('@3');

// yeild* 后面跟着一个新的iterator,后期执行到这的时候,会进入到新的generator中执行
function* generator1() {
yield 10;
yield 20;
}
function* generator2() {
yield 10;
yield* generator1(); // yield* 支持让我们进入另一个生成器函数中一步步执行
yield 20;
}
let itor = generator2();
console.log(itor.next()); //value:10 done:false
console.log(itor.next()); //value:10 done:false
console.log(itor.next()); //value:20 done:false
console.log(itor.next()); //value:20 done:false
console.log(itor.next()); //value:undefined done:true


const delay = (interval = 1000) => {
    return new Promise(resolve => {
        setTimeout(() => {
            resolve(`@@${interval}`);
        }, interval);
    });
};

通过以上想必对生成器有个初步的认识了,建议初学者自己敲打代码熟悉一下

下面我们将讲解生成器如何实现 async...await

现在我们有个需求:串行请求,有三个请求「请求需要的时间分别是 1000/2000/3000」?

先写一个延迟函数,模拟异步请求

const delay = (interval = 1000) => {
    return new Promise(resolve => {
        setTimeout(() => {
            resolve(`@@${interval}`);
        }, interval);
    });
};

这个延迟函数如果看不懂的话,下面的内容就不要看了,穿上黄马褂吧

1.通过 Promise 实现串行请求

delay(1000)
    .then(value => {
        console.log('第一个请求成功:', value);
        return delay(2000);
    })
    .then(value => {
        console.log('第二个请求成功:', value);
        return delay(3000);
    })
    .then(value => {
        console.log('第三个请求成功:', value);
    })
    .catch(reason => {
        console.log('任何请求失败,都执行这里:', reason);
    }); 

2.通过 async...await 实现串行请求

(async () => {
    try {
        let value = await delay(1000);
        console.log('第一个请求成功:', value);
        value = await delay(2000);
        console.log('第二个请求成功:', value);
        value = await delay(3000);
        console.log('第三个请求成功:', value);
    } catch (reason) {
        console.log('任何请求失败,都执行这里:', reason);
    }
})(); 

3.通过生成器函数实现串行请求


  const handle = function* handle() {
    let value = yield delay(1000);
    console.log('第一个请求成功:', value);
    value = yield delay(2000);
    console.log('第二个请求成功:', value);
    value = yield delay(3000);
    console.log('第三个请求成功:', value);
  };

  let iter = handle() // 获取迭代器对象
  /* done:是否执行完毕 value:获取的是每一次yield后面的值->Promise实例 */
  let { value, done } = iter.next() // {value: Promise, done: false}
  value.then(res => {
    console.log('res', res);
    // res 第一次请求成功的结果 @@1000
    let { value, done } = iter.next(res)
    value.then(res => {
      // res 第二次请求成功的结果 @@2000
      let { value, done } = iter.next(res)
      value.then(res => {
        // res 第三次请求成功的结果 @@1000
        let { value, done } = iter.next(res)
      })
    })
  })

虽然生成器函数也可以实现,但是每次调用都需要自己手动调用next方法,而且又有回调地狱的情况,使用起来一点都不友好,那怎么解决这个问题呢?

我们封装一个函数,实现递归调用next方法

 function AsyncFunction(generator, ...params) {
    let iter = generator(...params);
    // 基于递归的方法,通知Generator函数中的代码逐一执行
    const next = x => {
        let { done, value } = itor.next(x);
        if (done) return;
        if (!(value instanceof Promise)) value = Promise.resolve(value);
        // value.then(()=>{next(x)});
        value.then(next);
    };
    next();
};
 AsyncFunction(handle);

这个时候我们使用封装的AsyncFunction函数再去实现串行请求

function* handle() {
    let value = yield delay(1000);
    console.log('第一个请求成功:', value);
    value = yield delay(2000);
    console.log('第二个请求成功:', value);
    value = yield delay(3000);
    console.log('第三个请求成功:', value);
};

AsyncFunction(handle) // 这样就ok啦,是不是很方便

此时并不能看出来这个封装函数和async...await有啥相似之处,下面且看另一种写法

AsyncFunction(function* handle() {
    let value = yield delay(1000);
    console.log('第一个请求成功:', value);
    value = yield delay(2000);
    console.log('第二个请求成功:', value);
    value = yield delay(3000);
    console.log('第三个请求成功:', value);
  });

  (async () => {
    try {
      let value = await delay(1000);
      console.log('第一个请求成功:', value);
      value = await delay(2000);
      console.log('第二个请求成功:', value);
      value = await delay(3000);
      console.log('第三个请求成功:', value);
    } catch (reason) {
      console.log('任何请求失败,都执行这里:', reason);
    }
  })()

 

此时再看AsyncFunction是不是相当于async ,yield 相当于 await

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值