WebAssemblyWebAssembly 的 JavaScript API
作者:李俊才 (jcLee95):https://blog.csdn.net/qq_28550263?spm=1001.2101.3001.5343
本文地址:https://blog.csdn.net/qq_28550263/article/details/130879208
【介绍】:本文介绍 WebAssembly 的 JavaScript API。
上一节:《 如何加载和运行 WebAssembly 代码 》 | 下一节:《 暂无 》
目 录
- 2.2.1 instantiate() 函数
- 2.2.2 instantiateStreaming() 函数
- 2.2.3 compile() 函数
- 2.2.4 compileStreaming() 函数
1. 概述
1.1 引言
在之前的文章中我们讲解过将某些语言(如 Rust、 C++ 等)编译成 WebAssembly ,也简单讲解过 如何加载和运行 WebAssembly 代码 。
当加载了一个 .wasm 模块 之后,你就想要去使用它。WebAssembly 的 JavaScript API, 就为我们提供了这样的交互能力。 本文在前面已经对 WebAssembly 有一定的使用基础上,争取对 WebAssembly 的 JavaScript API 有全面把握,以为应用自如打好基础。
1.2 为什么需要用于 WebAssembly 的 JavaScript API
WebAssembly 是一种可移植、高性能的二进制格式,可以在现代 Web 浏览器中运行。虽然 WebAssembly 可以直接由编译器生成并在浏览器中运行,但为了能够与 JavaScript 交互和利用 Web 平台的功能,WebAssembly 需要使用 JavaScript API,比如用于操作 DOM,等等。
归纳起来,WebAssembly 目前仍然需要使用 JavaScript API 的主要原因有以下几点:
- 当前,WebAssembly 还没有直接访问浏览器 API 的能力。WebAssembly 本身只是一种二进制格式,没有直接访问浏览器 DOM、网络请求、图形渲染 等能力。通过 JavaScript API,WebAssembly 可以与 JavaScript 代码进行交互,并利用 JavaScript 来访问浏览器提供的功能。
- JavaScript API 提供了底层数据传输和交互的机制。WebAssembly 与 JavaScript 之间需要进行数据的传递和交互。JavaScript API 提供了一系列的函数和对象,用于在 WebAssembly 模块和 JavaScript 之间传递数据、调用函数、导入导出功能等。
1.3 ★ 浏览器 和 NodeJS 环境加载 wasm 模块 差异
本文后续介绍各个 API 中涉及大量的代码示例,这些示例都是假定在浏览器中使用的。实际上在实际开发中,也经常应用于 NodeJS 环境,这里会有一些不同。在这一小节我们提前把这里的问题讲清楚,后面则不再对该问题进行赘述。
1.3.1 浏览器环境下
浏览器环境下我们使用 Fetch API 起 HTTP 请求来获取 WebAssembly 模块,因为:
- WebAssembly 模块 通常是作为 独立的二进制文件存在,而不是内联在 HTML 文件中。因此,需要通过网络从服务器获取模块文件。
- WebAssembly 模块可能具有较大的文件大小,使用 HTTP 请求可以利用浏览器的缓存机制,减少模块的重复下载。
- Fetch API 提供了一种方便的方式来异步获取资源。它支持 Promise 和 async/await 语法,使得处理异步操作更加简洁和可读。使用 Fetch API 发起请求可以通过设置请求头、处理错误和响应等进行更灵活的控制。
总之,使用 Fetch API 发起 HTTP 请求来获取 WebAssembly 模块是为了从服务器异步获取模块文件,并能够灵活地处理请求和响应过程。这样可以实现模块的动态加载和更好的控制。
1.3.2 NodeJS 环境下
在 Node.js 环境中,由于没有浏览器的环境和 Fetch API,不能直接使用 fetch
函数来获取 WebAssembly 模块(除非你硬要搭建一个静态文件服务器,再来使用代码请求到模块代码来运行)。
通常情况下在 Node.js 环境中我们使用 fs
模块 来读取 模块文件 并 获取其 二进制数据。
具体的,在 NodeJS 中,你可以参考下面示例给出的方式 加载 WebAssembly 模块:
- 使用
fs.readFileSync
读取 WebAssembly 模块的二进制文件,并使用WebAssembly.compile
编译模块。
const fs = require('fs'); const buffer = fs.readFileSync('module.wasm'); const module = new WebAssembly.Module(buffer);
- 使用
WebAssembly.instantiate
或WebAssembly.instantiateStreaming
方法直接实例化 WebAssembly 模块。
const fs = require('fs'); const buffer = fs.readFileSync('module.wasm'); const module = new WebAssembly.Module(buffer); WebAssembly.instantiate(module).then(instance => { // ... });
2. WebAssembly JavaScript API 详解
2.1 API 概述
2.1.1 JavaScript 的 WebAssembly 对象是一个命名空间对象
在逐个讲解前,我们一定要先注意,类似于 Math 对象 或者 Intl 对象:
JavaScript 中,WebAssembly 不是一个构造函数(它不是一个函数对象),而是所有 WebAssembly 相关功能的 命名空间。——和大多数全局对象不一样。
一般我们主要通过 WebAssembly 对象上提供的:
- WebAssembly.instantiate() 函数 加载 编译好的 加载 WebAssembly 代码;
- WebAssembly.Memory() 构造函数 创建新的内存实例;
- WebAssembly.Table() 构造函数 创建新的表实例;
- WebAssembly.CompileError() 构造函数 来提供 WebAssembly 中的编译错误信息;
- WebAssembly.LinkError() 构造函数 来提供 WebAssembly 模块实例化期间的错误信息;
- WebAssembly.RuntimeError() 构造函数 来提供 WebAssembly 中的运行错误信息。
2.1.2 API 一览表
普通函数
API | 描述 |
instantiate() | 编译和实例化 WebAssembly 代码的主要的 API,它返回一个 Module 及其第一个 Instance 实例。 |
instantiateStreaming() | 直接从流式底层源编译和实例化 WebAssembly 模块,同时返回 Module 及其第一个Instance实例。 |
compile() | 把 WebAssembly 二次代码编译为一个 WebAssembly.Module,不进行实例化。 |
compileStreaming() | 直接从流式底层源代码编译 WebAssembly.Module,将实例化作为一个单独的步骤 |
用作构造器函数
这些函数用于通过 JavaScript 函数的 构造调用 以创建他们表示的同名对象实例,相当于基于类的面向对象编程语言中某个类的构造方法,因此用的都是大写字母开头。不了解的可以参考我的另外一篇博文 https://blog.csdn.net/qq_28550263/article/details/123418894。
API | 描述 | 说明 |
Global(descriptor , value ) |
创建一个新的 Global 对象实例。 | Global 对象表示一个全局变量实例,可以被 JavaScript 和 importable/exportable 访问 ,跨越一个或多个WebAssembly.Module 实例。他允许被多个 modules 动态连接。 |
Module(bufferSource ) |
创建一个新的 Module 对象实例。 | Module 对象包含已经由浏览器编译的无状态 WebAssembly 代码,可以高效地与 Worker 共享和多次实例化。 |
Memory(memoryDescriptor ) |
创建一个新的 Memory 对象实例。 | Memory 对象是一个可调整大小的ArrayBuffer或SharedArrayBuffer,用于保存 WebAssembly.Instance 访问的原始内存字节。 |
Table(tableDescriptor ) |
创建一个新的 Table 对象实例。 | |
CompileError(message , fileName , lineNumber ) |
创建一个新的 CompileError 对象实例。 | CompileError 对象表示 WebAssembly 解码或验证期间的错误。 |
LinkError(message , options ) |
创建一个新的 LinkError 对象实例。 | LinkError 对象指示模块实例化期间的错误(除了来自start函数的 traps 之外)。 |
RuntimeError(message , options ) |
创建一个新的 RuntimeError 对象实例。 | |
Instance(module , importObject ) |
创建一个新的 Instance 对象实例。 | Instance 对象包含所有 导出的 WebAssembly 函数,这些函数允许从 JavaScript 调用 WebAssembly 代码。 |
Tag(type ) |
创建一个新的 Tag 对象实例。 | .Tag对象定义了一种可以向 WebAssembly 代码抛出或从中抛出的 WebAssembly 异常类型。(这是一个新API) |
Exception(tag , payload , options ) |
创建一个新的 Exception 对象实例。 | Exception对象表示从 WebAssembly 抛出到 JavaScript 或 从 JavaScript 抛出到 WebAssembly 异常处理程序的 运行时异常。(这是一个新API) |
2.2 WebAssembly API 中的普通函数
2.2.1 instantiate() 函数
该方法从已编译的 WebAssembly 模块创建一个实例。
其语法格式为:
WebAssembly.instantiate(bufferSource, importObject)
其中参数:
bufferSource
:包含 WebAssembly 模块字节码的 ArrayBuffer、TypedArray 或 DataView。importObject
(可选):一个对象,用于传递给 WebAssembly 模块的导入对象。
例如:
// 发起对 WebAssembly 模块的 HTTP 请求 const response = await fetch('module.wasm'); // 将响应转换为 ArrayBuffer const buffer = await response.arrayBuffer(); // 使用 ArrayBuffer 创建 WebAssembly 模块实例 const module = await WebAssembly.instantiate(buffer); // 获取 WebAssembly 实例对象 const instance = module.instance;
2.2.2 instantiateStreaming() 函数
该方法通过流式加载已编译的 WebAssembly 模块,并创建一个实例。
其语法格式为:
WebAssembly.instantiateStreaming(source, importObject)
其中参数:
source
:一个表示可通过 HTTP 请求获取 WebAssembly 模块的 URL 字符串或 Response 对象。importObject
(可选):一个对象,用于传递给 WebAssembly 模块的导入对象。
例如:
// 流式加载并实例化 WebAssembly 模块 const module = await WebAssembly.instantiateStreaming(fetch('module.wasm')); // 获取 WebAssembly 实例对象 const instance = module.instance;
2.2.3 compile() 函数
该方法将 WebAssembly 模块的字节码编译成一个可执行的模块对象。
其语法格式为:
WebAssembly.compile(bufferSource)
其中参数:
bufferSource
:包含 WebAssembly 模块字节码的 ArrayBuffer、TypedArray 或 DataView。
例如:
// 发起对 WebAssembly 模块的 HTTP 请求 const response = await fetch('module.wasm'); // 将响应转换为 ArrayBuffer const buffer = await response.arrayBuffer(); // 将 ArrayBuffer 编译成可执行的 WebAssembly 模块对象 const module = await WebAssembly.compile(buffer);
2.2.4 compileStreaming() 函数
该方法通过流式加载 WebAssembly 模块的字节码,并将其编译成一个可执行的模块对象。其语法格式为:
WebAssembly.compileStreaming(source)
其中参数:
source
:一个表示可通过 HTTP 请求获取 WebAssembly 模块的 URL 字符串或 Response 对象。
例如:
const module = await WebAssembly.compileStreaming(fetch('module.wasm'));
2.3 WebAssembly API 中的构造函数
2.3.1 Module() 构造函数 与 Module 对象
WebAssembly.Module 对象的构造函数
WebAssembly.Module() 构造函数 用于创建一个 WebAssembly.Module 实例对象。它接受一个 ArrayBuffer 作为参数,并返回一个模块对象,用于后续的实例化操作。
该构造函数的类型签名即下一节 Module 对象的类型签名 中的 new 方法的签名,这里不重复给出。
例如:
fetch('module.wasm') .then(response => response.arrayBuffer()) .then(buffer => { const module = new WebAssembly.Module(buffer); // 使用模块进行实例化等操作 });
Module 对象解析
WebAssembly.Module 对象类型签名为:
var WebAssembly.Module: { new (bytes: BufferSource): Module; prototype: Module; customSections(moduleObject: Module, sectionName: string): ArrayBuffer[]; exports(moduleObject: Module): ModuleExportDescriptor[]; imports(moduleObject: Module): ModuleImportDescriptor[]; }
customSections 方法
该方法用于获取模块中自定义的节(section)。
其中参数:
moduleObject
是一个 WebAssembly.Module 对象,sectionName
是一个字符串,表示要获取的自定义节的名称。
该方法返回一个数组,这个数组包含了指定名称的自定义节的内容。
例如:
const response = await fetch('module.wasm'); // 将响应转换为 ArrayBuffer const buffer = await response.arrayBuffer(); // 将 ArrayBuffer 编译成可执行的 WebAssembly 模块对象 const module = await WebAssembly.compile(buffer); const customSections = WebAssembly.Module.customSections(module, 'custom_section_name'); console.log(customSections);
exports 方法
该方法用于获取模块中的导出项(exports)信息。
其中参数:
module
是一个 WebAssembly.Module 对象。
该方法返回一个数组,包含了模块中所有导出项的信息。
例如:
const response = await fetch('module.wasm'); // 将响应转换为 ArrayBuffer const buffer = await response.arrayBuffer(); // 将 ArrayBuffer 编译成可执行的 WebAssembly 模块对象 const module = await WebAssembly.compile(buffer); const customSections = WebAssembly.Module.customSections(module, 'custom_section_name'); console.log(customSections);
imports 方法
该方法用于获取模块中的导入项(imports)信息。
其中参数:
module
是一个 WebAssembly.Module 对象。
该方法返回一个数组,包含了模块中所有导入项的信息。
例如:
const response = await fetch('module.wasm'); // 将响应转换为 ArrayBuffer const buffer = await response.arrayBuffer(); // 将 ArrayBuffer 编译成可执行的 WebAssembly 模块对象 const module = await WebAssembly.compile(buffer); const imports = WebAssembly.Module.imports(module); console.log(imports);
2.3.2 Global() 构造函数 与 Global 对象
WebAssembly.Global 对象的构造函数
WebAssembly.Global() 构造函数 用于创建一个 WebAssembly.Global 实例对象。
该构造函数的类型签名即下一节 Global 对象的类型签名 中的 new 方法的签名,这里不重复给出。
Global 对象解析
Global 对象的类型签名为:
var WebAssembly.Global: { new (descriptor: GlobalDescriptor, v?: any): Global; prototype: Global; }
这个对象没有需要解释的其它方法。
2.3.3 Instance() 构造函数 与 Instance 对象
WebAssembly.Instance 对象的构造函数
WebAssembly.Instance() 构造函数 用于创建一个 WebAssembly.Instance 实例对象。它接受一个 WebAssembly.Module 对象和一个可选的导入对象作为参数,并返回一个实例对象,用于调用 WebAssembly 模块中的函数和访问导出的功能。
该构造函数的类型签名即下一节 Instance 对象的类型签名 中的 new 方法的签名,这里不重复给出。
例如:
fetch('module.wasm') .then(response => response.arrayBuffer()) .then(buffer => { // 创建 wasm 的 Module 对象实例 const module = new WebAssembly.Module(buffer); // 创建 wasm 的 Instance 对象实例 const instance = new WebAssembly.Instance(module, { /* imports */ }); // 使用实例调用导出的函数等操作 });
WebAssembly.Instance 对象
Instance 对象的类型签名为:
var WebAssembly.Instance: { new (module: Module, importObject?: Imports): Instance; prototype: Instance; }
这个对象没有需要解释的其它方法。
2.3.4 Memory() 构造函数 与 Memory 对象
WebAssembly.Memory 对象的构造函数
WebAssembly.Memory() 构造函数用于创建一个 WebAssembly.Memory 实例对象。它接受一个描述内存大小的页数作为参数,并返回一个内存对象,用于 WebAssembly 模块中的内存访问操作。
该构造函数的类型签名即下一节 Memory 对象的类型签名 中的 new 方法的签名,这里不重复给出。
例如:
const memory = new WebAssembly.Memory({ initial: 10, maximum
Memory 实例对象的 buffer 属性是一个可调整大小的 ArrayBuffer ,其内存储的是 WebAssembly 实例 所访问内存的原始字节码。
WebAssembly.Memory 对象
Memory 对象的类型签名为:
var WebAssembly.Memory: { new (descriptor: MemoryDescriptor): Memory; prototype: Memory; }
这个对象没有需要解释的其它方法。
2.3.5 Table() 构造函数 与 Table 对象
WebAssembly.Table 对象的构造函数
WebAssembly.Table() 构造函数用于创建一个 WebAssembly.Table 实例对象。Table() 构造函数主要传入的是以下两个参数:
- initial:指定表的初始大小(以元素数量表示),默认为 0。
- element:指定表中每个元素的类型(函数引用类型),默认为 {element: “anyfunc”}。
可以参考该对象类型签名及进行理解。该构造函数的类型签名即下一节 Table 对象的类型签名 中的 new 方法的签名,这里不重复给出。
例如:
// 创建一个初始大小为 10 的 WebAssembly 表,存储任意函数引用类型 const table = new WebAssembly.Table({ initial: 10 }); // 向表中添加函数引用 table.grow(2); // 扩展表的大小为 12 table.set(0, myFunction); // 将 myFunction 设置为索引为 0 的元素 table.set(1, anotherFunction); // 将 anotherFunction 设置为索引为 1 的元素 // 调用表中的函数 table.get(0)(); // 调用索引为 0 的函数
WebAssembly.Table 对象
Table 对象的类型签名为:
type TableKind = "anyfunc" | "externref"; interface TableDescriptor { element: TableKind; initial: number; maximum?: number; } var WebAssembly.Table: { new (descriptor: TableDescriptor, value?: any): Table; prototype: Table; }
TableKind
类型表示一个对象,表示表中每个元素的类型,可以是以下值之一:
- “anyfunc”: 任意函数引用类型
- “funcref”: 函数引用类型
这个对象没有需要解释的其它方法。
2.3.6 RuntimeError() 构造函数 与 RuntimeError 对象
WebAssembly.RuntimeError 对象的构造函数
WebAssembly.RuntimeError() 构造函数用于创建一个 WebAssembly.RuntimeError 实例对象。通过构造调用该对象,可以创建 WebAssembly 运行时错误(Runtime Error)。
其中参数:
message
:一个可选的字符串,表示错误消息。
例如:
// 创建一个自定义的 WebAssembly 运行时错误 const error = new WebAssembly.RuntimeError("Custom runtime error occurred."); throw error; // 抛出错误
WebAssembly.RuntimeError 对象
RuntimeError 对象用于在 WebAssembly 模块执行期间抛出自定义的运行时错误。通过 抛出自定义的 WebAssembly.RuntimeError,你可以在 WebAssembly 模块 的 执行期间捕获并处理特定的运行时错误情况。
该对象的类型签名为:
var WebAssembly.RuntimeError: { (message?: string): RuntimeError; new (message?: string): RuntimeError; prototype: RuntimeError; }
通过抛出自定义的 WebAssembly.RuntimeError,你可以在 WebAssembly模块的执行期间 捕获并处理特定的运行时错误情况。
【注】:WebAssembly.RuntimeError 是一个构造函数,用于创建错误对象。它不是 JavaScript 中内置的异常类型,而是用于在 WebAssembly 环境中表示运行时错误的特定对象。
这个对象没有需要解释的其它方法。
2.3.7 CompileError() 构造函数 与 CompileError 对象
WebAssembly.CompileError 对象的构造函数
WebAssembly.CompileError() 构造函数 用来创建一个 WebAssembly.CompileError 实例对象,以实现在编译 WebAssembly 模块时抛出自定义的编译错误。
其中参数:
message
:一个可选的字符串,表示错误消息。
例如:
// 创建一个自定义的 WebAssembly 编译错误 const error = new WebAssembly.CompileError("Custom compile error occurred."); throw error; // 抛出错误
WebAssembly.CompileError 对象
CompileError 对象的类型签名为:
var WebAssembly.CompileError: { (message?: string): CompileError; new (message?: string): CompileError; prototype: CompileError; }
通过抛出 自定义的 WebAssembly.CompileError,你可以在编译 WebAssembly 模块时捕获并处理特定的编译错误情况。
【注】:WebAssembly.CompileError不是 JavaScript 中内置的异常类型,而是用于在 WebAssembly 环境中表示编译错误的特定对象。
2.3.8 LinkError() 构造函数 与 LinkError 对象
WebAssembly.LinkError 对象的构造函数
WebAssembly.LinkError() 构造函数 用于创建一个 WebAssembly.LinkError 实例对象,从而在链接 WebAssembly 模块时抛出自定义的链接错误。
其中参数:
message
:一个可选的字符串,表示错误消息。
例如:
// 创建一个自定义的 WebAssembly 链接错误 const error = new WebAssembly.LinkError("Custom link error occurred."); throw error; // 抛出错误
WebAssembly.LinkError 对象
LinkError 对象的类型签名为:
var WebAssembly.LinkError: { (message?: string): LinkError; new (message?: string): LinkError; prototype: LinkError; }
通过抛出自定义的 WebAssembly.LinkError,你可以在 链接 WebAssembly 模块时 捕获并处理特定的链接错误情况。
【注】:WebAssembly.LinkError 是一个构造函数,用于创建错误对象。它不是 JavaScript 中内置的异常类型,而是用于在 WebAssembly 环境中表示链接错误的特定对象。
这个对象没有需要解释的其它方法。
2.3.9 Exception() 构造函数 与 Exception 对象
WebAssembly 技术仍在不断发展和演进中,新的功能和规范也可能随着时间推移而推出。目前谷歌 Chrome 浏览器等浏览器已经支持该对象,不过不能确保所有就有的浏览器都被部分支持。笔者尚未实际使用过,因此先介绍过来,待日后获取更多信息再对本小节内容进行调整。
该对象的 W3C 文档位置为:https://webassembly.github.io/exception-handling/js-api/#runtime-exceptions
WebAssembly.Exception 对象的构造函数
WebAssembly.Exception() 构造函数 用于创建一个 WebAssembly.Exception 实例对象 其语法格式为:
new Exception(tag, payload) new Exception(tag, payload, options)
其中:
tag
:用于定义 WebAssembly.Tag 中每个值的预期数据类型payload。payload
:包含异常负载的一个或多个数据字段的数组。元素必须匹配 中相应元素的数据类型tag
。如果有效负载中的数据字段数量与其类型不匹配,TypeError 则会抛出异常。options
(可选):具有以下可选字段的对象:
traceStack
:如果 Exception 可能有一个堆栈跟踪附加到它的 stack 属性,则为true
,否则为false
。默认情况下,这是false
(如果未提供options
或options.traceStack
)。
WebAssembly.Exception 对象
它表示表示从 WebAssembly 抛出到 JavaScript 或从 JavaScript 抛出到 WebAssembly 异常处理程序的运行时异常。
Exception 访问抛出的异常的参数需要用于创建 的同一 Tag。提供了方法来测试异常是否与特定标记匹配,以及通过索引获取特定值(如果异常与指定标记匹配)。
当共享关联标签时,JavaScript 和其他客户端代码只能访问 WebAssembly 异常值,反之亦然(您不能只使用恰好定义相同数据类型的另一个标签)。如果没有匹配的标记,异常可以被捕获并重新抛出,但它们无法被检查。
用法例如:
// 创建标记并使用它来创建异常 const tag = new WebAssembly.Tag({ parameters: ["i32", "f32"] }); const exception = new WebAssembly.Exception(tag, [42, 42.3]);
实例属性 stack
返回异常的堆栈跟踪,或 undefined。
WebAssembly.Exception.prototype.stack
非标准:此功能是非标准的,不在标准轨道上。不要在面向 Web 的生产站点上使用它:它不适用于每个用户。实现之间也可能存在很大的不兼容性,并且行为可能会在未来发生变化。
2.3.10 Tag() 构造函数 与 Tag 对象
WebAssembly 技术仍在不断发展和演进中,新的功能和规范也可能随着时间推移而推出。目前谷歌 Chrome 浏览器等浏览器已经支持该对象,不过不能确保所有就有的浏览器都被部分支持。笔者尚未实际使用过,因此先介绍过来,待日后获取更多信息再对本小节内容进行调整。
该对象的 W3C 文档位置为:https://webassembly.github.io/exception-handling/js-api/#dom-tag-tag
WebAssembly.Tag 对象的构造函数
WebAssembly.Tag() 构造函数 用于创建一个 WebAssembly.Tag 实例对象 其语法格式为:
new WebAssembly.Tag(type)
WebAssembly.Tag 对象
type() 方法
该方法返回为标签定义数据类型数组的对象(在其构造函数中设置)。例如:
// 该例子为 MDN 给出,但是目前笔者测试的浏览器似乎并不支持。 const tagToImport = new WebAssembly.Tag({ parameters: ["i32", "f32"] }); console.log(tag.type()); // Console output: // Object { parameters: (2) […] } // parameters: Array [ "i32", "i64" ] // <prototype>: Object { … }