当我们在一个Web应用中使用WebAssembly,最终的目的要么是执行wasm模块的入口程序(通过start指令指定的函数),要么是调用其导出的函数,这一切的前提需要创建一个通过WebAssembly.Instance对象表示的wasm模块实例(源代码)。

一、wasm模块实例化总体流程

二、利用WebAssembly.Module创建实例

三、通过字节内容创建创建实例

四、利用XMLHttpRequest加载wasm模块

五、极简编程方式

一、wasm模块实例化总体流程

虽然编程模式多种多样,但是wasm模块的实例化总体采用如下的流程:

  • 步骤一:下载wasm模块文件;
  • 步骤二:解析文件并创建通过WebAssembly.Module类型表示的wasm模块;
  • 步骤三:根据wasm模块,结合提供的导入对象,创建通过WebAssembly.Instance类型表示的模块实例。

二、利用WebAssembly.Module创建实例

我们照例通过一个简单的实例来演示针对wasm模块加载和模块实例创建的各种编程模式。我们首先利用WebAssembly Text Format(WAT)形式定义如下一个wasm程序,定义的文件名为app.wat。如代码所示,我们定义了一个用于输出指定浮点数(i64)绝对值的导出函数absolute。绝对值通过f64.abs指令计算,具体得输出则通过导入的print函数完成。

(module
(func $print (import "imports" "print") (param $op f64) (param $result f64))
(func (export "absolute") (param $op f64)
(local.get $op)
(f64.abs (local.get $op))
(call $print)
)
)

我们通过指定wat2wasm (源代码压缩包种提供了对应的.exe)命令(wat2wasm app.wat –o app.wasm)编译app.wat并生成app.wasm后,定义如下这个index.html页面,作为宿主程序的JavaScript脚本完全按照上面所示的步骤完成了针对wasm模块实例的创建。

<html>
<head></head>
<body>
<div id="container"></div>
<script>
var print = (op, result) => document.getElementById("container").innerText = `abs(${op}) = ${result}`;
fetch("app.wasm")
.then((response) => response.arrayBuffer())
.then(bytes => {
var module = new WebAssembly.Module(bytes);
var instance = new WebAssembly.Instance(module, {"imports":{"print": print}});
instance.exports.absolute(-3.14);
})
</script>
</body>
</html>

具体来说,我们调用fetch函数将app.wasm文件下载下来后,我们将获得的字节内容作为参数调用构建函数创建了一个WebAssembly.Module对象。然后将这个Module对象和创建的导入对象({"imports":{"print": print}})作为参数调用构造函数创建了一个WebAssembly.Instance对象,该对象正是我们需要的wasm模块实例。我们从模块实例中提取并执行导出的absolute函数。导入的print函数会将绝对值计算表达式以如下的形式输出到页面中。

除了调用构造函数以同步(阻塞)的方式根据WebAssembly.Module对象创建WebAssembly.Instance对象外,我们还可以调用WebAssembly.instantiate静态方法以异步的方式“激活”wasm模块实例,它返回一个Promise<WebAssembly.Instance>对象。

var print = (op, result) => document.getElementById("container").innerText = `abs(${op}) = ${result}`;
fetch("app.wasm")
.then((response) => response.arrayBuffer())
.then(bytes => {
var module = new WebAssembly.Module(bytes);
return WebAssembly.instantiate(module, { "imports": { "print": print } });
})
.then(instance => instance.exports.absolute(-3.14));

三、通过字节内容创建创建实例

静态方法WebAssembly.instantiate还提供了另一个重载,我们可以直接指定下载wasm模块文件得到的字节内容作为参数。这个重载返回一个Promise<WebAssembly.WebAssemblyInstantiatedSource>对象,WebAssemblyInstantiatedSource对象的instance属性返回的正是我们需要的wasm模块实例。

var print = (op, result) => document.getElementById("container").innerText = `abs(${op}) = ${result}`;
fetch("app.wasm")
.then((response) => response.arrayBuffer())
.then(bytes => WebAssembly.instantiate(bytes, {"imports":{"print": print}}))
.then(result =>result.instance.exports.absolute(-3.14));

四、利用XMLHttpRequest加载wasm模块

fetch函数是我们推荐的用于下载wasm模块文件的方式,不过我们一定义要使用传统的XMLHttpRequest对象也未尝不可。上面的三种激活wasm模块实例的方式可以采用如下的形式来实现。

var print = (op, result) => document.getElementById("container").innerText = `abs(${op}) = ${result}`;
const request = new XMLHttpRequest();
request.open("GET", "app.wasm");
request.responseType = "arraybuffer";
request.send(); request.onload = () => {
var bytes = request.response;
var module = new WebAssembly.Module(bytes);
var instance = new WebAssembly.Instance(module, {"imports":{"print": print}});
instance.exports.absolute(-3.14);
};

上面演示的利用创建的WebAssembly.Module对象和导入对象调用构造函数创建WebAssembly.Instance的同步形式。下面则是将二者作为参数调用静态方式WebAssembly.instantiate以异步方式激活wasm模块实例的方式。

var print = (op, result) => document.getElementById("container").innerText = `abs(${op}) = ${result}`;
const request = new XMLHttpRequest();
request.open("GET", "app.wasm");
request.responseType = "arraybuffer";
request.send(); request.onload = () => {
var bytes = request.response;
WebAssembly
.instantiate(request.response, {"imports":{"print": print}})
.then(result => result.instance.exports.absolute(-3.14));
};

下面演示WebAssembly.instantiate静态方法的另一个重载。

五、极简编程方式

其实我们有“异步到位”的方式,那就是按照如下的形式执行静态方法WebAssembly.instantiateStreaming。该方法的第一个参数用于提供下载.wasm模块文件的PromiseLike<Response>对象,第二个参数则用于指定导入对象。该方法同样返回一个Promise<WebAssembly.WebAssemblyInstantiatedSource>对象,WebAssemblyInstantiatedSource的instance属性返回的正是我们所需的wasm模块实例。

var print = (op, result) => document.getElementById("container").innerText = `abs(${op}) = ${result}`;
WebAssembly
.instantiateStreaming(fetch("app.wasm"), {"imports":{"print": print}})
.then(result => result.instance.exports.absolute(-3.14))

WebAssembly核心编程[1]:wasm模块实例化的N种方式的更多相关文章

  1. VB 核心编程及通用模块开发 笔记1

    前言:学习任何编程语言.编程工具的目的不外乎解决生活问题,能够快速.高效解决问题的方式就是不错的方式,至于选择什么“工具”,众位看官看着办,本人虽然有过3年vb开发经验,但是一直没有深入学习,现已购买 ...

  2. 【Spring】的【bean】管理(XML配置文件)【Bean实例化的三种方式】

    Bean实例化的三种方式 说明:通过配置文件创建对象就称为Bean实例化. 第一种:使用类的无参构造创建(重点) 实体类 package com.tyzr.ioc; public class User ...

  3. Java并发编程:线程间协作的两种方式:wait、notify、notifyAll和Condition

    Java并发编程:线程间协作的两种方式:wait.notify.notifyAll和Condition 在前面我们将了很多关于同步的问题,然而在现实中,需要线程之间的协作.比如说最经典的生产者-消费者 ...

  4. Spring中bean实例化的三种方式

    之前我已经有好几篇博客介绍Spring框架了,不过当时我们都是使用注解来完成注入的,具体小伙伴可以参考这几篇博客(Spring&SpringMVC框架案例).那么今天我想来说说如何通过xml配 ...

  5. 19、Java并发编程:线程间协作的两种方式:wait、notify、notifyAll和Condition

    Java并发编程:线程间协作的两种方式:wait.notify.notifyAll和Condition 在前面我们将了很多关于同步的问题,然而在现实中,需要线程之间的协作.比如说最经典的生产者-消费者 ...

  6. Spring bean管理器 bean实例化的三种方式

    bean实例化的三种方式实现 第一种:使用类的无参数构造方法创建(常用 重要) 第一种实例化方式最常用,实例化类时会通过调用无参构造方法创建.示例代码如下: package spring.com.Us ...

  7. python 模块调用的几种方式

    在python里面又很多模块,或者引用第三方模块,python 模块调用的几种方式,下面详细解说 1,import 模块名 2,from 模块 import  模块里面的小功能 3,from  模块 ...

  8. spring实例化bean三种方式

    我看了这篇博文<https://www.cnblogs.com/zhanglei93/p/6221546.html>,以及自己实践总结了关于spring实例化bean对象的3种方式. 一. ...

  9. Bean实例化的三种方式

    1. 构造器实例化 spring容器通过bean对应的默认的构造函数来实例化bean. 2. 静态工厂方式实例化 首先创建一个静态工厂类,在类中定义一个静态方法创建实例. 静态工厂类及静态方法: pu ...

  10. spring学习(03)之bean实例化的三种方式

    bean实体例化的三种方式 在spring中有三中实例化bean的方式: 一.使用构造器实例化:(通常使用的一个方法,重点) 二.使用静态工厂方法实例化: 三.使用实例化工厂方法实例化 第一种.使用构 ...

随机推荐

  1. WPF ElementHost 内存泄露问题

    无意中发现,WPF ElementHost 控件如果未正确释放,会存在内存泄露问题.解决方法如下: xaml代码: <Grid x:Name="grid"> <W ...

  2. Codeforces Round #624 (Div. 3) (A~D,CD Good)

    比赛链接:Here 1311A. Add Odd or Subtract Even 签到题, \(a > b\) 时必须做做减法,如果差值为偶数的话只需要 \(1\) 次不然做一次减法后再做一次 ...

  3. Centos7 kubeadm安装k8s

    安装环境准备 关闭防火墙 systemctl stop firewalld systemctl disable firewalld 关闭selinux sed -i 's/enforcing/disa ...

  4. 报错:for..in loops iterate over the entire prototype chain, which is virtually never what you want.

    for..in loops iterate over the entire prototype chain, which is virtually never what you want. 意思是使用 ...

  5. 腾讯视频客户端 MP4 下载

    腾讯视频直接使用客户端下载视频,得到的是 QLV 文件,这种加密视频文件只能通过腾讯视频客户端播放.最新版的腾讯客户端下载的 QLV 文件,使用各种转码软件都不能正常转码.从服务器下载的 TS 文件一 ...

  6. jmap 查看jvm内存大小并进行dump文件内存分析

    本文为博主原创,未经允许不得转载: 1.jmap的使用 Jmap 可以用来查看内存信息,实例个数以及占用内存大小. jmap -histo[:live] 打印每个class的实例数目,内存占用,类全名 ...

  7. 【开源分享】基于Html开发的房贷计算器,模仿新浪财经

    房贷计算器是一种房贷计算的在线计算Web应用,按用户选择的贷款类型.贷款金额.期限.利率可计算得出每月月供参考.支付利息.还款总额这些信息.本文模仿新浪财经开发的房贷计算器. 作品预览 https:/ ...

  8. LaTeX 公式识别问题

    问题 想要方便的图片公式识别工具来写Latex(论文)/markdown(笔记)文件 工具推荐 1.mathpix 识别成功率最高(无论是多行,表格表现都非常良好),最好用的工具,但是收费高且付费麻烦 ...

  9. 【收集】C & C++

    序 链接 备注 1 C语言0长度数组(可变数组/柔性数组)详解_CHENG Jian的博客-CSDN博客_0数组   2 C 语言参考 | Microsoft Learn   3 C++ 语言参考 | ...

  10. electron打包,使用electron-packager

    构建项目可以使用electron-forge构建,但是这个东西打包比较坑,mac运行报错,win下会有缓存机制,也就是热更新无效 所以选择使用electron-packager打包 sudo npm ...