![]()
2015年,一位前端工程师在GitHub issue里写下:「计算斐波那契数列时,页面冻结了3秒,用户以为浏览器崩溃了。」这条issue获得了1400多个,却等了9年才等到一个像样的答案。JavaScript的单线程模型像一条设计精良的单行道——异步I/O开得飞快,CPU密集型任务却堵成停车场。
Worker API(工作线程接口)理论上能解决问题,但用起来像在没有导航的陌生城市开车。你需要手动创建脚本文件、处理消息传递、管理线程生命周期,还要小心闭包陷阱。一个40行的计算任务,配套代码能写到200行。社区统计过,npm上带「worker」关键词的封装库超过800个,说明这不是某个人的痛点,是整个生态的集体妥协。
从「能跑就行」到「像写同步代码」
今年4月发布的puru库把这段代码:
const worker = new Worker('./fib.js');worker.postMessage(40);worker.onmessage = ({ data }) => console.log(data);
压缩成了一行:
const { result } = spawn(() => fibonacci(40));
表面看是语法糖,实际重构了心智模型。开发者不再需要把逻辑拆到另一个文件,不需要手写消息协议,甚至不需要理解Transferable Objects(可传输对象)的内存规则。puru在编译阶段把函数序列化,注入到Worker沙箱里执行,再把结果Promise化返回。
这个设计有个刻意为之的限制:函数不能捕获外部闭包。乍看是缺陷,实则是防呆机制——它强迫你意识到Worker运行在隔离上下文,共享状态会出bug。很多开发者第一次用Worker时,都踩过「闭包变量突然变成undefined」的坑。puru用编译时错误替代了运行时惊喜。
当Promise.all()不够用的时候
CPU密集型任务很少单打独斗。图像处理要流水线,科学计算要分片,数据分析要MapReduce。Promise.all()能并发,但管不了背压、超时、取消信号——这些在服务端是基本功,在浏览器里得自己造轮子。
puru从Go语言借了chan()和WaitGroup(等待组)两个原语。看这段生产者-消费者模型:
const input = chan(50);const output = chan(50);for (let i = 0; i < 4; i++) { spawn(async ({ input, output }) => { for await (const n of input) { await output.send(n * 2); } });}
通道在这里充当机械缓冲,解耦了生产速度和消费速度。50的缓冲区意味着生产者最多领先消费者50个任务,超出的部分会阻塞等待——这是显式的背压控制,raw Worker(原生工作线程)里完全没有。但缓冲区大小没有银弹:太小会频繁阻塞,太大可能内存溢出。puru把这个权衡留给开发者,文档里建议「根据任务粒度和可用内存测试调整」。
序列化的代价与边界
不是所有函数都能塞进spawn()。puru使用structured clone algorithm(结构化克隆算法)序列化参数和函数体,这意味着你不能传DOM节点、不能传函数引用、不能传循环引用的对象。一个试图在Worker里操作canvas的开发者会收到清晰的报错,而不是静默失败。
这个限制划清了适用边界:puru适合纯计算任务(加密、压缩、矩阵运算),不适合需要频繁与主线程交互的场景。游戏引擎的物理模拟、视频编辑器的实时预览,这些需要每帧同步的状态,Worker通信开销会吃掉所有收益。
库的维护者在README里写得很诚实:「这不是Worker的替代品,是80%场景的默认选择。」剩下的20%,你仍然需要掌握Transferable Objects和SharedArrayBuffer(共享内存缓冲区)。
为什么现在才出现
Worker API 2010年就进了草案,2014年全浏览器支持。14年里,社区不是没有尝试过封装——从早期的webworker-threads到近年的comlink,每个都解决了一部分问题。但comlink主打的是「让Worker像代理对象」,仍然要求你把逻辑拆到单独文件;puru第一次做到了「写在哪就在哪运行」。
这个突破依赖两个基础设施成熟:ES Module(ECMAScript模块)让动态加载不需要全局污染,Top-level await(顶层等待)让异步初始化不再丑陋。2020年前的JavaScript写不出puru这样的API。
npm周下载量从发布时的300涨到现在的4700,增速曲线像早期webpack。但更重要的信号是生态位——Vite团队正在评估内置集成,Deno(一个JavaScript运行时)的第三方注册表已经收录。一个库有没有未来,不看star数,看构建工具愿不愿意背书。
puru的GitHub仓库里有个被置顶的讨论串:「你们会支持WASM(WebAssembly)互操作吗?」维护者的回复是:「有计划,但想先观察社区怎么用通道模式。」这个回答本身说明了一件事——他们还没想清楚边界在哪里,但愿意跟着真实需求走,而不是画路线图。
下一个版本会砍掉什么功能,又会因为什么用户反馈加回来?
特别声明:以上内容(如有图片或视频亦包括在内)为自媒体平台“网易号”用户上传并发布,本平台仅提供信息存储服务。
Notice: The content above (including the pictures and videos if any) is uploaded and posted by a user of NetEase Hao, which is a social media platform and only provides information storage services.