浅谈异步发展史(三):昙花一现的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的精髓对于每一位前端工程师来说都是不可或缺的技能。

相关推荐
橙子家1 小时前
浏览器缓存之【身份与会话管理】:Cookies 和 Private state tokens
前端
To_OC2 小时前
LC 49 字母异位词分组:想到哈希表很简单,选对 key 才是精髓
javascript·算法·leetcode
最新资讯动态2 小时前
HDC 2026 | 对话鲸鸿动能:存量时代,品牌如何夺回营销“主动权”?
前端
最新资讯动态2 小时前
游戏出海,从产品走向体系
前端
最新资讯动态2 小时前
20人团队跑出百万DAU、大厂也来抢量:谁在鸿蒙生态跑出加速度
前端
最新资讯动态2 小时前
千万开发者背后,鸿蒙商业化的B面
前端
爱勇宝4 小时前
AI 时代:智商决定起点,情商决定走多远
前端·ai编程
kyriewen4 小时前
用了半年 Claude Code 后,我尝试关掉它写了一周代码——结果比想象中严重
前端·javascript·ai编程
IT_陈寒5 小时前
Vite的静态资源打包让我熬夜到三点,这坑千万别跳
前端·人工智能·后端
山河木马6 小时前
矩阵专题0-webGL中的矩阵
javascript·webgl·计算机图形学