浅谈异步发展史(三):昙花一现的generator

前言

在我前面两篇文章中简单聊了聊异步的发展,从最开始的回调地狱,到后来的promise,再到手撕一个简单的promise。今天我们来聊聊generator构造器

正文

什么是generator

generator也是实现异步的一个方法,虽然它甚至不如直接使用promise来的快,可以说是十分的不好用。但是能够充分理解generator对学习async函数来说也是不可缺少的一环。

在前言中也提到了,generator是一个构造器函数,那么我们怎么去使用呢?或者说如何去实例化一个generator对象出来?答案很简单

js 复制代码
function* foo() {
    //function之后加一个星号就变成生成器函数
}

正如上面代码所示,只需要在function后面添加一个*就可以。而构造器函数能够默认返回一个generator对象,即使这个函数里面什么也没有

在generator对象中有一个十分重要的关键字------"yield"。这可以说是generator实现异步操作的关键,yield允许用户通过调用generator对象自带的next方法去手动控制代码的执行。例如:

js 复制代码
function* g() {
    let a = 1
    console.log(a);
    let b = yield a++
    console.log(b);
    let c = yield a++
    console.log(a);
}

let gen = g()

console.log(gen.next(), 'next1');//读取到1,再释放当前yield
console.log(gen.next(5), 'next2');
console.log(gen.next(), 'next3');

当generator内部存在yield去暂停函数代码的执行时,generator返回的对象则会有一些不同的变化

在这个对象中我们分别拿到了value和done。done好理解,代表generator内部是否执行完毕,那么value又是啥?根据上面的代码输出不难发现,value拿到的是紧跟在yield后面的值且默认值为undefined。也就是说每当我们调用一次next,就会得到一个新的generator对象,里面有两个键值对,分别代表yield后面的值,以及内部代码是否执行完毕。

为什么说是"昙花一现的generator"

首先要说明的是,所谓的昙花一现是指generator构造函数在异步处理方面是如此,而在其他地方并不一定也是昙花一现的。在很多人看来generator在异步处理这块最大的贡献就是为async和await这两块语法糖打下了坚实的基础。因为仅通过generator自身去实现异步的话,属实是有点反人类了。除此之外,Generator函数在推出之初,并没有得到广泛的社区支持和工具支持。相比之下,Promise和async/await很快就被各种框架和库所采纳,并且得到了广泛的推广和使用。不仅如此, Generator函数在某些情况下可能会导致性能问题,尤其是与async/await相比,后者在某些JavaScript引擎中可能有更好的性能优化。

最后,我个人认为没落的另一个原因在于不好理解,很容易造成混乱。在之前就提到过yield会拿到紧跟在后面的值,不仅如此,yield的值会被next传入的参数代替。

就以刚刚的代码为例子,按照正常思维来说,b的打印应该是a++完成之后的结果,但是当我们第二次调用next的时候传入了一个参数'5',所以导致了整个yield以及后面的代码的值被替换成了5。那我不传,就可以了吗?

js 复制代码
console.log(gen.next(), 'next2');

显然依旧是于事无补,因为next会默认返回一个undefined。

Await的前身---co

在co出现之前,要通过generator去实现异步还得搭配上promise,简直就是脱裤子放屁。就在generator快要消失的时候,出现了一个非常好用的co模块,无论是实现方式还是使用方式简直和后来出现的async/await一模一样,很难不怀疑js官方是不是直接抄了co模块。

Co的使用

首先,通过命令npm install co安装co模块

其次,在代码中引入co

js 复制代码
var co = require('co');

接着,将能够返回promise对象的函数放入generator构造器中。

js 复制代码
function a() {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            console.log('a完成');
            resolve()
        }, 1000);
    })
}

function b() {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            console.log('b完成');
            resolve()
        }, 500);
    })
}

function* c(f1, f2) {
    yield f1()
    yield f2()
}

最后,通过co处理异步

js 复制代码
co(c(a, b))

结果如下图:

总结

Generator函数作为JavaScript语言的重要特性,不仅极大提升了代码的可读性和可维护性,还为异步编程提供了新的思路和解决方案。通过灵活运用yield、yield*以及与Promise、async/await的结合,开发者可以更高效地管理复杂的数据流和异步逻辑,构建出更加健壮和优雅的应用程序。随着JavaScript生态的不断进化,掌握Generator的精髓对于每一位前端工程师来说都是不可或缺的技能。

相关推荐
蓝天白云下遛狗16 分钟前
goole chrome变更默认搜索引擎为百度
前端·chrome
come1123439 分钟前
Vue 响应式数据传递:ref、reactive 与 Provide/Inject 完全指南
前端·javascript·vue.js
前端风云志1 小时前
TypeScript结构化类型初探
javascript
musk12121 小时前
electron 打包太大 试试 tauri , tauri 安装打包demo
前端·electron·tauri
翻滚吧键盘2 小时前
js代码09
开发语言·javascript·ecmascript
万少2 小时前
第五款 HarmonyOS 上架作品 奇趣故事匣 来了
前端·harmonyos·客户端
OpenGL2 小时前
Android targetSdkVersion升级至35(Android15)相关问题
前端
rzl023 小时前
java web5(黑马)
java·开发语言·前端
Amy.Wang3 小时前
前端如何实现电子签名
前端·javascript·html5
海天胜景3 小时前
vue3 el-table 行筛选 设置为单选
javascript·vue.js·elementui