写点什么

14 行 JavaScript 教你使用 WebAssembly

2018 年 5 月 09 日

看新闻很累?看技术新闻更累?试试下载 InfoQ 手机客户端,每天上下班路上听新闻,有趣还有料!

作为一种比较新的 web 技术,WebAssembly 可能会对 web 开发带来巨大的影响。随着 2 月 MVP(Minimum Viable Product)版本的发布,WebAssembly 的基本特性开始稳定,本文通过一个简单的示例来演示如何在页面上调用一个 C++ 函数。

基本组成

要编写一个可以在浏览器中运行的 WebAssembly 应用,主要分为两个部分,可编译为 WebAssembly 的源语言和加载用的 JavaScript。

整个流程大致可以分为:

  1. 编写代码:用常用语言编写一个工程,可以是 C、C++、Rust 等。其他各种语言编译成 WebAssembly 可以参照这里
  2. 编译:将源码编译成 WebAssembly 的 wasm 文件。
  3. 引入:将之前生成的的 wasm 文件引入到前端工程中。
  4. 实例化:编写一段异步 JavaScript 代码加载和实例化 wasm 文件,使其他 JavaScript 文件可以无障碍使用。

当然,这只是一个非常简化的流程,但是对于诸如本文的示例项目已经足够。首先,我们会使用 C++ 编写一个简单的函数,然后使用在线工具将其编译成 wasm 文件(这样我们不用下载和安装任何编译工具),最后通过 14 行 JavaScript 代码加载并实例化。

上述过程完成之后,我们就能够通过 JavaScript 代码来调用之前通过 C++ 编写的函数了,是不是很神奇?

编写代码

首先我们会编写一个 C++ 函数,当然了,前面提到过,这里不需要使用任何本地的编译链。取而代之的是一个 WebAssembly Explorer 的工具。它是一个类似 CodePen 的工具,可以左侧一列贴入 C++ 代码,编译后下载对应的 wasm 文件。当然,类似的在线 WebAssembly 工具还有 Mozilla 提供的 WebAssembly Studio

首先打开 WebAssembly Explorer 页面,然后在左侧粘贴如下 C++ 代码:

复制代码
int squarer(int num) {
return num * num;
}

正如之前提到的,这是一个非常简单的示例,对于之前没有学习过 C++ 的人也没有任何难度。

编译

下一步就是点击编译(“compile”)按钮,就能看见如下图所示界面,C++ 代码被编译撑了 WAT 格式( WebAssembly text format )的文本。

这里不详述 WAT 格式,展示该格式内容主要是因为相对于 wasm 的二进制格式,这种文本格式更易读。通过 WAT 文件中可以了解 JavaScript 和 WebAssembly 是如何交互的。例如本例中,在 export 关键字后面有 _z7squareri 这个导出的函数名,因为编译器对 C++ 函数名的修饰(mangling)。

这里需要特别注意,因为后面使用 JavaScript 对 C++ 函数进行调用的时候需要依赖这个名字。当然,为了避免这种问题,可以将编译器选择为 C99 或者在函数外层增加 extern "C"块:

复制代码
extern "C" {
  int squarer(int num) {
    return num * num;
  }
}

这样编译之后函数名称不会有变化。编译成的 WAT 文件内容如下图:

此时导出的函数名和原 C++ 函数中的函数名保持一致。

引入

现在点击下载(“download”)按钮,并将下载的 wasm 文件重命名成 squarer.wasm。

创建一个文件夹,包含刚才下载的 squarer.wasm 文件,并新建下面两个文件:

  • index.html:包含标准网页的模板。
  • scripts.js:暂时这是一个空文件,下面就将编写实例化使用的 JavaScript。

实例化

目前为止,HTML 标准还不支持将 wasm 文件像 JavaScript 文件那样通过 script 标签直接引入。因此为了加载和实例化 WebAssembly,需要通过 JavaScript 异步的调用 WebAssembly API 来完成这些操作。一共分为三步:

  1. 将 wasm 文件加载到数组缓冲区( array buffer )。
  2. 将加载的数组编译成 WebAssembly 模块
  3. 实例化 WebAssembly 模块。

实例化之后,JavaScript 就可以直接调用刚才 WAT 文件中 export 的函数和内存了。

运行

最后当然是期待人心的运行了。先来看看 scripts.js 内容:

复制代码
let squarer;
function loadWebAssembly(fileName) {
return fetch(fileName)
.then(response => response.arrayBuffer())
.then(bits => WebAssembly.compile(bits))
.then(module => { return new WebAssembly.Instance(module) });
};
loadWebAssembly('squarer.wasm')
.then(instance => {
squarer = instance.exports._Z7squareri;
console.log('Finished compiling! Ready when you are...');
});

这里的 loadWebAssembly 函数加载本地的 wasm 文件并进行实例化,最终返回一个 WebAssembly 模块实例。随后从模块中获取导出的函数,随后即可直接调用。这里需要特别注意,导出的函数名和刚才在 WAT 文件中看见的符号需要一致。由于 C++ 编译器对函数名的修饰,建议在对所有需要导出给 JavaScript 的函数,都加上 extern "C"块,避免函数名混乱导致的不匹配。

在 index.html 文件中引入 scripts.js 之后,可以在控制台尝试调用 squarer 函数:

通过这个示例,我们了解了如何将C++ 代码编译成WebAssembly 并在浏览器中执行。我们可以将更多的C、C++、Rust 语言编写的工程放到浏览器中运行,以获得更高的执行效率。当然,还是要再次声明,这里只是一个简单的示例,如果函数的参数不是数字,还会有其他问题。随着WebAssembly 工具链和集成工具的不断完善,相信编写基于WebAssembly 的项目一定会更加方便。


感谢覃云对本文的审校。

给InfoQ 中文站投稿或者参与内容翻译工作,请邮件至 editors@cn.infoq.com 。也欢迎大家通过新浪微博( @InfoQ @丁晓昀),微信(微信号: InfoQChina )关注我们。

2018 年 5 月 09 日 19:002809

评论 1 条评论

发布
用户头像
好的,谢谢!
2020 年 04 月 19 日 15:52
回复
没有更多了
发现更多内容

mongodb 源码实现、调优、最佳实践系列-百万级高并发mongodb集群性能数十倍提升优化实践(下篇)

杨亚洲(专注mongodb及高性能中间件)

MySQL nosql mongodb 架构 分布式 分布式数据库mongodb

详细分析定制企业应用的价格

Learun

敏捷开发 快速开发 软件架构

“一个APP竟然可以适配这么多设备?!”《优酷响应式布局技术全解析》开放下载

破绽

阿里巴巴 阿里云 开发者 优酷 电子书

数字货币交易所开发方案,撮合系统搭建app源码

WX13823153201

深入理解 JVM 垃圾回收算法 - 复制算法

Java架构师迁哥

SpringBoot有多重要?面试用SpringBoot把面试官唬住了要30k都行!

Java架构之路

Java 程序员 架构 面试 编程语言

NET-Core中的配置文件操作

为体验更多

C# .net .net core ASP.NET Core

阿里P8大牛呕心沥血总结整理的《Java面经手册》,通过实践的方式向你深度讲解Java核心知识点

Java架构之路

Java 程序员 架构 面试 编程语言

华为云数据安全中心正式公测,8大核心数据安全能力守护你的数据

华为云开发者社区

华为 安全 数据

CloudQuery v1.1.1 修复版本发布

CloudQuery社区

数据库 sql 安全 工具软件

程序员的美丽假期(并不)

Philips

敏捷开发 快速开发

LAXCUS大数据集群操作系统:一个分布式分时共享E级系统软件(三)

陈泽云

人工智能 大数据 计算机网络 操作系统 网络

技术实践丨GaussDB(DWS)运维管理功能“升级”的原理和使用

华为云开发者社区

运维 数据 集群

想要高效搭建企业信息平台?教你轻松选择开发框架!

Marilyn

敏捷开发 快速开发

出炉!华为18A自爆SpringCloud微服务分布式笔记

996小迁

Java 编程 架构 面试 SpringCloud

面试大厂被面试官用MyBatis怼到“哑口无言”?这份MyBatis源码笔记助你吊打面试官!

Java架构之路

Java 程序员 架构 面试 编程语言

华为鲲鹏专家解读:90%代码如何移植到鲲鹏平台

华为云开发者社区

软件 鲲鹏

Github惊现高星神作,两份算法宝典让你横扫大厂算法面试题

云流

编程 程序员 算法 计算机

websocket 是怎么连接的

lockdown56

nginx 网络 HTTP websocket

GO 类型接口及反射间的转换

superman

go golang golang新手

云计算简史(上)- 15分钟读完15年

明道云

成为一名合格的技术类产品经理

小清新同学

产品经理

让黑产无处遁形 京东智联云推出风险识别服务

京东智联云开发者

人工智能 学习 风险识别

日常工作问题集锦

hasWhere

直播预告 | 云时代的数据库客户端——CloudQuery最佳实践

CloudQuery社区

数据库 sql 安全 工具软件

从分布式到微服务成长手册,助我面试跳槽斩获字节Offer

Java架构追梦

Java 学习 架构 面试 架构微服务

诸多老牌数据仓库厂商当前,Snowflake如何创近12年最大IPO金额

华为云开发者社区

数据仓库 数据 存储

LeetCode题解:98. 验证二叉搜索树,递归,JavaScript,详细注释

Lee Chen

LeetCode 前端进阶训练营

台湾地区为什么会丢包高?

德胜网络-阳

Week 4 命题作业及总结

阿泰

你听过CatBoost吗?本文教你如何使用CatBoost进行快速梯度提升

计算机与AI

Python 学习 优化

InfoQ 极客传媒开发者生态共创计划线上发布会

InfoQ 极客传媒开发者生态共创计划线上发布会

14行JavaScript教你使用WebAssembly-InfoQ