异步无处不在:Generator 异步(五)
(。・∀・)ノ゙嗨,我是你稳定更新、持续输出的勾勾。
关于异步的知识点,终于要结束了!
本篇就来重点说说如何将 Generator 写进异步中。
Generator 异步方案
将调用 ajax 的代码写到生成器函数的 yield 后面,每次的异步执行,都要在 yield 中暂停,调用的返回结果是一个 Promise 对象。
我们可以从迭代器对象的 value 属性获取到 Promise 对象,然后使用 .then 进行链式调用处理异步结果。
结果处理的代码叫做执行器,就是具体负责运行逻辑的代码。
function ajax(url) {
……
}
// 声明一个生成器函数
function * fun(){
yield myAjax('./d1.json')
yield myAjax('./d2.json')
yield myAjax('./d3.json')
}
// 返回 遍历器对象
var f = fun();
// 生成器函数的执行器
// 调用 next 方法,执行异步代码
var g = f.next();
g.value.then(data=>{
console.log(data);
// console.log(f.next());
g = f.next();
g.value.then(data=>{
console.log(data)
// g.......
})
})
而执行器的逻辑中,是相同嵌套的,因此可以写成递归的方式对执行器进行改造:
// 声明一个生成器函数
function * fun(){
yield myAjax('./d1.json')
yield myAjax('./d2.json')
yield myAjax('./d3.json')
}
// 返回 遍历器对象
var f = fun();
// 递归方式 封装
// 生成器函数的执行器
function handle(res){
if(res.done) return;
res.value.then(data=>{
console.log(data)
handle(f.next())
})
}
handle(f.next());
然后,再将执行的逻辑,进行封装复用,形成独立的函数模块。
function co(fun) {
// 返回 遍历器对象
var f = fun();
// 递归方式 封装
// 生成器函数的执行器
function handle(res) {
if (res.done) return;
res.value.then(data => {
console.log(data)
handle(f.next())
})
}
handle(f.next());
}
co(fun);
封装完成后,我们再使用时只需要关注 Generator 中的 yield 部分就行了。
function co(fun) {
……
}
function * fun(){
yield myAjax('./d1.json')
yield myAjax('./d2.json')
yield myAjax('./d3.json')
}
此时你会发现,使用 Generator 封装后,异步的调用就变得非常简单了。
但是,这个封装还是有点麻烦,有大神帮我们做了这个封装,相当强大:https://github.com/tj/co ,感兴趣可以研究一下。
而随着 JS 语言的发展,更多的人希望类似 co 模块的封装能够写进语言标准中,而我们直接使用这个语法规则就行了。
其实你也可以对比一下,使用 co 模块后的 Generator 和 async 这两段代码:
// async / await
async function callAjax(){
var a = await myAjax('./d1.json')
console.log(a);
var b = await myAjax('./d2.json');
console.log(b)
var c = await myAjax('./d3.json');
console.log(c)
}
// 使用 co 模块后的 Generator
function * fun(){
yield myAjax('./d1.json')
yield myAjax('./d2.json')
yield myAjax('./d3.json')
}
你应该也发现了,async 函数就是 Generator 语法糖,不需要自己再去实现 co 执行器函数或者安装 co 模块。
写法上将 * 星号去掉换成放在函数前面的 async,把函数体的 yield 去掉,换成 await。
简直完美!
async function callAjax(){
var a = await myAjax('./d1.json')
console.log(a);
var b = await myAjax('./d2.json');
console.log(b)
var c = await myAjax('./d3.json');
console.log(c)
}
callAjax();
我们再来看一下 Generator。
相信下面的代码,你能很轻松地阅读。
function * f1(){
console.log(11)
yield 2;
console.log('333')
yield 4;
console.log('555')
}
var g = f1();
g.next();
console.log(666);
g.next();
console.log(777);
代码运行结果:
带着 Generator 的思路,我们再回头看看那个 async 的面试题。
请写出以下代码的运行结果:
setTimeout(function () {
console.log('setTimeout')
}, 0)
async function async1() {
console.log('async1 start')
await async2();
console.log('async1 end')
}
async function async2() {
console.log('async2')
}
console.log('script start')
async1();
console.log('script end')
运行结果:
是不是恍然大明白呢(●'◡'●)。
推荐阅读:
前端人因为 Vue3 的 Ref-sugar 提案打起来了!
点点“赞”和“在看”,保护头发,减少bug。