异步无处不在:Generator 异步(五)

勾勾的前端世界

共 3210字,需浏览 7分钟

 · 2021-01-15

(。・∀・)ノ゙嗨,我是你稳定更新、持续输出的勾勾。



关于异步的知识点,终于要结束了!


本篇就来重点说说如何将 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')


运行结果:



是不是恍然大明白呢(●'◡'●)。


推荐阅读:

异步无处不在:同步模式和异步模式(一)

异步无处不在:回调函数(二)

异步无处不在:Promise 破除“回调地狱”(三)

异步无处不在:终极解决方案(四)

Redux 续集 | 如何搞定其中的异步操作?

技术人年度总结 | 2020,注定不平凡

是我 Web 端配不上阿里了。

前端人因为 Vue3 的 Ref-sugar 提案打起来了!


点点“”和“在看”,保护头发,减少bug。

浏览 5
点赞
评论
收藏
分享

手机扫一扫分享

举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

举报