对于网络平台而言,WebAssembly 的出现无疑是意义重大的,它能让各种语言编写的代码以接近原生的速度在 Web 中运行——那么在各种语言中,JavaScript 会是其中最流行的吗?
原文链接:https://thenewstack.io/will-javascript-become-the-most-popular-webassembly-language/
未经允许,禁止转载!
作者 | Mary Branscombe
译者 | 弯月 责编 | 郑丽媛
出品 | CSDN(ID:CSDNnews)
WebAssembly 是从浏览器中发展而来的,因此很多人认为 JavaScript 很适合 WebAssembly。然而,最初 WebAssembly 的目标是编译其他语言,以便开发人员可以在浏览器中通过 JavaScript 与这些语言交互(生成在浏览器中运行的 Wasm 编译器会创建 Wasm 模块,以及允许 Wasm 模块访问浏览器 API 的 JavaScript 填充程序)。
如今,有几款在服务器端运行的 WebAssembly 非浏览器运行时(包括 Docker 的 Wasm 支持),在这类运行时中,Wasm 模块实际上是在 JavaScript 运行时(如 V8)中运行的,因此即使 WebAssembly 运行时日渐普及,与 JavaScript 保持一致仍然很重要。
Wasm 是支持多种语言的,永远都是,例如最近 Wasm 的发展重点是支持 Rust、Python、Ruby 和 .NET 等许多语言。但 JavaScript 也是世界上最流行的编程语言之一,很多重要提升都是为了使用 JavaScript 作为编写可编译为 WebAssembly 模块的语言,还有很多人在尝试将提高 JavaScript 性能的经验应用到 Wasm。
开发者的需求
Fermyon 在发布用于为 Spin 框架构建组件的 SDK 时,首先使用了 .NET,然后使用了 JavaScript 和 TypeScript。首席执行官 Matt Butcher 进行客户调研,希望找出他们喜欢使用的语言:“你对什么语言感兴趣?你用什么语言编写程序?你喜欢用什么语言写程序?基本上,JavaScript 和 TypeScript 都排在前三名。”(开发人员选择的第三种语言是 Rust,可能是因为 Wasm 的 Rust 工具普遍比较成熟;另外 .NET、Python 和 Java 也很受欢迎。)
Butcher 表示,Suborbital 在推出用于构建服务器端扩展的 JavaScript 支持时,也看到了类似反应,JavaScript 很快就成为了构建服务器端扩展时最受欢迎的开发语言。
目前,在 Fermyon 的客户中,31% 的人希望支持 JavaScript,20% 希望支持 TypeScript。虽然不清楚支持这两种语言的开发人员是同一伙人,还是各代表一半的开发人员,但很明显 JavaScript 大幅领先。对此,我们感到很惊讶——我们原以为 JavaScript 社区会反对 WebAssembly 支持 JavaScript,但结果完全相反。
Butcher 曾认为,编写 WebAssembly 的语言之间会出现更激烈的竞争,但这些反应改变了他的想法:“这些语言之间不会形成竞争。WebAssembly 将变成每个了解 JavaScript 的开发人员都可以编写和运行的一个场所。说到底,人们还是想要 JavaScript。”
Butcher 的观点符合阿特伍德定律(任何可以用 JavaScript 来写的应用,最终都将用 JavaScript 来写),同时字节码联盟技术指导委员会主任 Bailey Hayes 也提到了 Gary Bernhardt 著名的 JavaScript 的诞生和死亡(他预言了WebAssembly这类运行时的诞生,而且还将JavaScript比作可以在世界末日中幸存下来的蟑螂):
“Rust 很难学,虽然它很受欢迎,但学习曲线十分陡峭。如果是刚开始学的新手,我希望他们从已掌握的知识入手让开发人员使用他们熟悉的工具探索 WebAssembly 这样的新领域,可以提高他们的效率,并打造更好的软件生态系统”,“很明显,我们都看好 JavaScript,因为它是世界上最受欢迎的语言,我们希望让尽可能多的人使用 WebAssembly!”
开发人员想用 JavaScript 做什么
Butcher 将 WebAssembly 的使用分为四大类:浏览器应用程序、云应用程序、物联网应用程序和插件应用程序——而 JavaScript 涉及以上所有领域。
“我们看到开发人员用 JavaScript 和 WebAssembly 为大量面向 JavaScript 的前端编写后端,为前端的 React 应用提供数据,然后使用 JavaScript 后端来实现数据存储或处理。”
Hayes 指出,服务器端 Wasm 有明显的优势:“使用服务器端 JavaScript 的人可直接上手编写服务器端 Wasm,从而通过更少的代码构建更快的程序,他们可以享受各种好处,且不会遇到任何阻力。”
Butcher 认为还有一些在 WebAssembly 中使用 JavaScript 的建议非常有创意,“关于为什么希望 WebAssembly 支持 JavaScript,有人阐述了一个非常有趣的原因:你可以创建一个更安全的 JavaScript 沙盒,然后在 WebAssembly 内部执行任意不受信任的代码,并使用浏览器版本的 JavaScript 接口来防止不受信任的 JavaScript 干扰其他受信任的代码。”
能够在 Wasm 沙盒中隔离不受信任的代码片段是嵌入式 WebAssembly 的常见用例,SingleStore、Scylla、Postgres、TiDB 和 CockroachDB 一直在尝试使用 Wasm 存储过程。
Fastly 的 js-compute 运行时是运行在 WebAssembly 上的 JavaScript,用于边缘计算;Suborbital 专注于插件;Shopify 最近添加了 JavaScript 作为利用 WebAssembly 函数自定义后端的首选语言;而前段时间发布的 RedPanda 也支持 WebAssembly(也是使用 JavaScript)。
RedPanda 的 WebAssembly 模块公开了一个 JavaScript API,用于编写如何在其与 Kafka 相兼容的流媒体平台上存储数据的策略,首席执行官 Alex Gallego 表示,这是因为 JavaScript 的灵活性以及在开发人员中间的流行度。
灵活性对于平台开发人员来说很重要。Alex Gallego 指出:“在设计新产品时,最大的困难是设计长期的 API。一旦你做出承诺,人们就会将这些代码投入生产,然后你就永远无法再删除,如果做出错误的决定,就会陷入困境无法自拔。从框架开发人员的角度来看,你可以利用 JavaScript 超快地迭代,并根据社区的反馈相对更容易地修改接口,因为它是一种动态语言。”
开发人员可以借助 JavaScript 获得熟悉的业务逻辑编程模型,例如屏蔽证件号码、查找特定年龄段的用户或对 IP 地址进行信用评分,所有这些工作都不需要分布式存储和流媒体管道复杂性方面的专家来做。“多线程、向量化指令、IO、设备处理、网络吞吐量的可扩展性维度,所有核心的棘手问题仍由底层平台处理。”
JavaScript:流行度与高性能
流行度是吸引开发人员使用 JavaScript 编写 WebAssembly 模块的常见原因。
在一项新服务推出时,开发人员必然没有任何使用经验,但是因为他们了解 JavaScript,所以更容易快速上手。Gallego 指出,这为平台提供了一个庞大的潜在客户社区。
“在使用 WebAssembly 时,你可以混合多种编程语言,但我认为选择 JavaScript 才是正确的。这门语言非常容易,很友好,有很多包,还有数不尽的开发者教程。随着公司的发展,你需要招揽更多人才,一般这都很有挑战性,但雇佣 JavaScript 开发人员相对要容易得多。”
“在公开 API 时,你必须寻找合适的设计,对我来说,融入最大的编程社区是一个非常关键的决定。”
Fastly 的 Guy Bedford 表示,“JavaScript 是使用最广泛的语言之一,由于其采用率,这门语言有着举足轻重的作用。同样,WebAssembly 拥有很多优势,例如具有安全性、高性能以及可移植性等,因此可以部署到不同的环境。很多公司都在用 WebAssembly 做些非常有趣的事情,而他们希望支持来自这些现有生态系统的开发人员。”
JavaScript 有一些明显的优势,Bucher 指出:“门槛低,学习的资源种类繁多,你可以通过 npm 获得数不尽的库。”我们之所以考虑将 JavaScript 与 WebAssembly 结合到一起,库是其中一个很重要的原因。
“如果你开发了一个非常擅长矩阵乘法的库,那么就应该竭尽所能利用好开发这个库所花费的大量时间。”Gallego 建议,凭借这些优势,JavaScript 在 Wasm 中的地位就像 SQL 的地位一样。
JavaScript 长达 20 年的优化也是一个很大的优势。他指出,“人们为这个生态系统投入了大量资源,专家们会想方设法加快网站的渲染速度。V8 JavaScript 引擎背后的编程团队甚至加入了 Java 垃圾收集器创建者这样的优秀人才。致力于提高 JavaScript 性能的程序员大概是这个世界上最专注于此的人,这股力量比其他任何东西都强大。”
“我认为,这就是 JavaScript 长盛不衰的原因,其背后站着一群聪明有才华的人,他们不仅注重规范级别,也很有行动力。”
他指出,“JavaScript 的单线程性能非常棒,非常适合边缘计算,WebAssembly 与 JavaScript 的组合将成为一个真正可行的成熟应用程序开发工具。”
同样,Butcher 考虑在 WebAssembly 云上对 React 应用程序进行服务器端渲染,以应对无法在浏览器中运行大量 JavaScript 的设备。
他表示,“V8 拥有非常出色的性能优化。即便是 Python 和 Ruby 这类的成熟语言也没有像 JavaScript 一样如此注重性能优化,一而再,再而三地提升速度。”
“性能非常重要,而且 JavaScript 运行时很容易采用……在我看来,大家确实很想要一个可以在 WebAssembly 中运行的版本。这样就可以继续长期以来一直享有的好处。”
但如今 WebAssembly 还没有做好准备为主流 JavaScript 开发人员所使用。
Bedford 警告说,“JavaScript 的入门门槛很低,不需要拥有学位或大量经验,它是一种非常容易理解的语言。但如果你是一名 JavaScript 开发人员而且想使用 WebAssembly,那么真正掌握使用方法并不容易。”
将 JavaScript 引入 Wasm 的不同方式
你可以使用 JavaScript 来编写 WebAssembly 模块,但 Cosmonic 首席执行官 Liam Randall 表示:“在未来几个月,字节码联盟将推出重大更新,提供更多的 JavaScript 支持。”
Randall 说道:“今年构建、创建和操作组件的能力方面取得了重大进展,其中最重要的两种语言分别是 Rust 和 JavaScript。”
目前,最流行的方法是使用 QuickJs 解释器(最初由 Shopify 采用并推广,这个包非常小,只有 210KB),许多 WebAssembly 运行时中都包含这个包。例如,Shopify 的 Javy 和 Fermyon 的 spin-js-sdk 都使用了Quickjs 与 Wasmtime 运行时(早期与 TypeScript 绑定,但尚未将 JavaScript 作为官方支持的语言),而且还有一个面向 CNCF WasmEdge 运行时的 QuickJS 版本,既支持在 WebAssembly 中使用 JavaScript,而且也允许在 JavaScript 中调用 C/C++ 和 Rust 函数。
QuickJs 支持 ECMAScript 2020 的大部分功能,包括字符串、数组、对象以及支持它们的方法、异步生成器、JSON 解析、正则表达式、ES 模块和可选的运算符重载、BigDecimal 和 BigFloat。所以,这个包可以运行大部分JavaScript代码。除了比较小之外,这个包还非常快,并为运行 JavaScript 提供了良好的性能——但它不支持 JIT。
使用 QuickJs 可以有效地捆绑在 JavaScript 运行时中,而且这种简约有一个很大的好处,Hayes 指出:“通常包的规模越大,性能就无法做到完美,但大多数情况下依然可以正常使用,我看到很多地方都采用了这个包。”
Fermyon 的 JavaScript SDK 建立在基于QuickJs的Javvy 之上,同时还使用了 Wizer 预初始化来保存代码初始化后的快照,从而加快 QuickJs 的启动时间。Butcher 解释道,“Wizer 可以加快 .NET 在 WebAssembly 上的运行速度,它从运行时开始,加载 .NET 的所有运行时环境,然后将其作为新的 WebAssembly 模块写回磁盘。我们发现使用QuickJs也可以采用相同的方法。”
“在允许spin构建时,SDK会获取 JavaScript 运行时,获取源文件,使用 WIZER 对其进行优化,然后将所有这些打包并发送到一个新的 WebAssembly 二进制文件中。”
通过预优化解释型语言的代码来提高速度的思路听起来很熟悉,因为大多数浏览器的 JavaScript 引擎就采用了这种方法。“引擎在解释JavaScript的同时将JavaScript文件发送给优化器,只需几毫秒,就可以从解释模式切换到编译优化模式。”
“不为人知的是,实际上WebAssembly集合了我们从 JavaScript、Java、.NET 积累的一切经验。我们在过去的15~20年间使用这些语言积累的经验的基础之上,构建了WebAssembly。”
添加 JIT
Shopify 还与 Igalia 签约,将 Mozilla JavaScript 引擎 SpiderMonkey 引入 Wasm;而 Fastly 则采用了 compontentize-js 法,使用 SpiderMonkey 以在浏览器中以高速模式运行 WebAssembly 的 JavaScript 代码,由 JIT 负责编译 JavaScript 代码并在 WebAssembly 解释器中运行。
尽管 WebAssembly 模块具备充分的可移植性,可以在许多不同的地方使用,但将多个 Wasm 模块组合成一个程序非常不容易。Wasm 中的类型支持很原始,各种模块可能需要的不同 WebAssembly 功能,而这些功能被分组到不同的“世界”(如 Web、云和 CLI),并且每个模块通常都会定义自己的本地地址空间。
Bedford 表示,“WebAssembly 的问题在于,获得某个二进制文件后,实际上你得到的只是非常低级的绑定函数,接下来你需要付出大量努力才能将所有二进制文件连起来。你必须专门花精力连接每种语言,需要对输入输出的数据进行复杂的变换处理,所以你必须是一个非常有经验的开发人员才能知道如何处理。”
WebAssembly 的组件模型添加了依赖项描述以及高级的、独立于语言的接口,用于传递值和指针。这些接口解决了上述所说的“完全不共享内存空间的高级封装问题”。
他解释道,“你有的不只是一个盒子,而是一个带有接口的盒子,盒子之间可以互相交谈。你可以拥有函数和不同类型的结构以及对象结构,你还可以拥有跨越组件边界的数据结构。”
Compontentize-js 就建立于此基础之上,允许开发人员使用任意绑定。“我们可以根据你的绑定和想要运行的 JavaScript 模块,给你一个 WebAssembly 二进制文件,它代表整个 JavaScript 运行时和拥有这些绑定的引擎。我们可以非常快速地提供这个二进制文件,而且可以生成非常复杂的绑定。”
这个过程不需要很多额外的 WebAssembly 构建步骤,JavaScript 开发人员可以使用熟悉的工具,并通过 npm 安装库。
尽管从规模上看,SpiderMonkey 比 QuickJs 更大(Bedford 估计带有 JavaScript 运行时的二进制文件和开发人员的 JavaScript 模块大约为5~6MB),但依然非常小,足以快速初始化,可用于边缘计算硬件。
这种方式同样使用 Wizer 来优化初始化性能,这样就可以缩短冷启动时间。“我们会在调用函数之前,预先初始化所有 JavaScript,所以JavaScript引擎不需要进行初始化处理。一切都已使用 Wizer 进行了预初始化。”
这不是提前(Ahead Of Time,AOT)编译,但今年晚些时候或明年,omponentize-js 将使用 Bedford 建议的部分评估技术进行更高级的运行时优化,这样就可以实现AOT。“因为你知道绑定了哪些函数,所以可以使用二村映射评估解释器,并获得这些函数的编译版本,作为SpiderMonkey本身评估解释器的一部分。”
Compontentize-js 是字节码联盟的一个名叫jco项目的一部分,jco 是一个为 WebAssembly 打造的JavaScript 组件工具,目前还是处于实验阶段的 JavaScript 组件工具链,并非该 JavaScript 运行时特定的工具。Bedford 解释说,“我们的基本思路是,构建一个更通用的工具,无论将 WebAssembly 放在何处,都可以使用 JavaScript 编写一小段代码。”
Randall 指出,Jco 是一个“彻头彻尾的 JavaScript 新体验”项目,这表明我们可以期待在 wasmtime 的下一个版本中看到更成熟的 JavaScript 和 Rust 组件版本。重要的是要注意,这些仍处于实验阶段,WebAssembly 组件模型尚未发布,Bedford 称 componentize-js 为研究,而非预发布软件:“我们的目标是为处于该领域最前沿的开发人员提供这些功能,目前只是迈出了第一步。”
实验性的 lightjs 针对的也是 WebAssembly 组件模型,他们创建的 Wasm 接口类型绑定可以共享 JavaScript 的类型和定义。到目前为止,wit-bindgen 生成器(为希望编译为 WebAssembly 并与组件模式一起使用的程序开发人员提供语言绑定)仅支持编译语言,C/C++、Rust、Java 和 TinyGo,因此添加JavaScript之类的解释型语言可能具有一定的挑战性。
虽然 spin-js-sdk 可以专门为 Spin HTTP 触发器生成绑定,但 SlightJs 的目标是为开发人员提供Wasm 接口类型绑定。最终,它将成为微软 SpiderLightning 项目的一部分,该项目将为开发人员提供使用 Wasm 接口类型构建云原生应用程序时所需的功能,为运行使用 SpiderLightning 的 Wasm 应用程序的轻量级命令行工具添加 JavaScript 支持。
目前,SlightJS 使用的是 QuickJs,因为QuickJs的性能更好,但随着 SpiderMonkey 的改进,将来可能会转变,Butcher 指出了 JIT 风格的 JavaScript 运行时具备的性能优势。QuickJs 本身在很大程度上取代了早期的可嵌入 JavaScript 引擎 Duktape。
Bedford 表示,“目前相关的活动呈爆炸式增长,该领域正在加速发展。”
JavaScript 与 Wasm 的共同提升
TC39 ECMAScript 工作组副主席 Daniel Ehrenberg 建议,你可以将以上这些方法想象成“JavaScript 脚本在上层,而WebAssembly 在下层”,但还有一种方法是“JavaScript 和 WebAssembly 并排放置,而底层是 JavaScript 虚拟机。”
后一种方法是 Bloomberg 和 Igalia 长期以来的努力方向,这种建议旨在实现 JavaScript 与 WebAssembly 之间的高效交互,例如引用类型字符串可以降低WebAssembly 程序使用 JavaScript 字符串的难度,而 WebAssembly GC 的垃圾收集可以简化内存管理。
TC39 联合主席兼 Bloomberg 基础设施与工具团队负责人 Rob Palmer 解释说,字符串在两种语言之间更好地协同工作可以提高效率。“目前二者还无法真正做到高效,因为在两个域之间复制字符串的开销超过了 WebAssembly 提供的速度提升。”
WebAssembly GC 给 JavaScript 带来的不仅是 JavaScript 的弱引用和 finalization registry,还提供了 WebAssembly 线性内存与基于 JavaScript 堆的内存之间的最小限度的互操作性,从而允许编译一些 Wasm 程序。Ehrenberg 解释道,“WebAssembly 不仅具有线性内存,而且还可以分配几个不同的垃圾收集分配对象,这些对象指向彼此,具有完全自动的内存管理。你只需要追踪引用,‘死掉’的东西都会自动消失。”
在 WASI 中支持线程,通过并行化提高性能,并提供对现有库的访问,这些工作尚处于早期阶段 (最初仅适用于 C,目前尚不清楚如何与组件模型协同工作),但这两个 WebAssembly 提案完成得非常好,希望很快就能在浏览器中推出,以帮助到一系列开发人员。
“这在一定程度上能够方便我们将 Kotlin 之类的语言编译成 WebAssembly,而且效率高于自己动手分配内存,此外还能方便我们在 JavaScript 和 WebAssembly 的并排架构中实现零拷贝的内存共享。”
对于服务器端 JavaScript,Ehrenberg 表示很欣慰看到两种方法之间互相协调的迹象,这两种方法最初似乎朝着不同的方向发展:WinterCG API 旨在方便在服务器端环境中使用 Web 功能,而 WASI 则旨在为 WebAssembly 提供更强大的 IO 功能。
他指出,“我们希望能够在 Deno 中使用 WinterCG API,同时也希望能够在 Shopify 的 JavaScript 环境和 Fastly 的 JavaScript 环境中使用 WinterCG API,后两种环境是使用 WASI 在 WebAssembly 之上实现的。目前人们正在努力在 WebAssembly 之上实现 JavaScript,他们正在研究 JavaScript 能否支持 WinterCG API,然后这些 WinterCG API 能否在 WASI 中实现。”
多语言 Wasm 的发展前景
JavaScript 的灵活性使其成为探索组件化以及可组合性的好方法,我们可以利用 JavaScript 为 WebAssembly 组件模型提供更多可能性,而这一切如今还处于萌芽状态。
JavaScript 与 Rust 将成为构建模块化 WebAssembly 体验的首选语言,Randall 预测将来这种体验将普及到所有语言,开发人员可以混合不同语言编写的多个 WebAssembly 组件,并将它们组合在一起创建新的应用程序。
“我可以使用高性能且安全的 Rust 来构建云组件,就像 wasmCloud 那样,将不太复杂的组件组合起来,用 JavaScript 编写面向用户的代码。我可以使用来自世界各地不同的 JavaScript 组件并将它们结合在一起,也可以使用 Rust 编写的组件,还可以通过许多不同的方式重构这些组件。”
Bedford 表示赞同,“你可以让 Rust 与 JavaScript 对话,在沙盒中运行代码,或者用一个高度优化的 Rust 组件做一些繁重的工作,然后用 JavaScript 编写高层组件。”
compontentize-js 允许你使用 JavaScript,并将其捆绑为 WebAssembly 组件;而 Jco 工具链以及类似工具(如同样依赖于组件模型的 cargo-component)则可以通过这种方式使用多语言。
Beford 解释道,”必须由某个人熟悉 Rust 应用程序并编写一些 JavaScript——编写并维护 JavaScript 的绑定生成。而使用组件模型就不需要考虑JavaScript 了,他们只需关注组件模型,提供多种语言的支持,而 JavaScript 开发者只需使用组件即可。“
“这就是组件模型为这些工作流程带来的便利性。有人可以用 Rust 编写组件,而你可以很方便地在 JavaScript 环境中使用这些组件。然后在浏览器之外,JavaScript 开发人员也可以一展所长。”
他指出,Rust 开发人员也可以通过这种方式使用 JavaScript 组件,“Jco 是一个 JavaScript 组件工具链,它支持使用 JavaScript 创建和运行 JavaScript 组件。”
Hayes 建议,将来开发者可以通过 wasm-compose 库将两个组件结合起来。未来几年内,组件模型将成为 WebAssembly 中一个非常有趣的探索方向:
“如果你熟悉 JavaScript 和 Rust,可以将两大语言生态系统结合在一起,二者之间可以互操作,人们则可以选择最好的库或工具。我非常期待 WebAssembly 的组件,因为从理论上讲,它可以打破前端与后端工程师以及语言生态系统之间的孤岛。”
推荐阅读:
▶阿里云刘伟光:2万字解读金融级云原生
▶对话凯文·凯利:AI 会取代人的 90% 技能,并放大剩余的 10%
▶时薪15美元的ChatGPT外包工人,干的都是苦力活