iT邦幫忙

2018 iT 邦幫忙鐵人賽
DAY 19
2
Modern Web

謙虛,踏實的Web Assembly練習系列 第 19

[練習 18] 在node.js環境中執行WebAssembly

  • 分享至 

  • xImage
  •  

node.js對WebAssembly的支援

相關支援其實在目前的LTS版本已經沒問題了(寫這篇文章時,是8.9.4 LTS),所以只要從nodejs.org網站上下載並安裝,就可以跑WebAssembly。我沒有去查Release Note,所以不是很確定從哪個版本開始直接支援,哪個版本要透過--expose-wasm來打開。

node.js環境所支援的,是跟WebAssembly的Javascript API一致的介面,所以可以直接透過這個介面來使用。但是我希望之前寫的wasm_util.js可以同時支援網頁及node.js環境,所以稍微調整了一下程式:

(function(global) {
	let _fetch;
	let node_context = false;
	if(typeof exports !== 'undefined') {
		if(typeof module !== 'undefined' && !!(module.exports)) {
			_fetch = function(path) {
				let fs = require('fs');
				return new Promise((resolve, reject) => {
					fs.readFile(path, (err, buf) => {
						if(!!err) {
							reject(err);
							return;
						}
						resolve({
							arrayBuffer: () => {
								return new Promise((resolve, reject) => {
									resolve(buf);
								});
							}
						});
					})
				});
			}
			node_context = true;
		} else {
			throw new Exception('module.exports not supported?');
		}
	} else {
		_fetch = fetch;
	}
	(function(fetch) {
		if(node_context) {
			exports = module.exports = Wasm;
		} else {
			global.Wasm = Wasm;
		}
		function Wasm(_url) {
			let module = null;
			let url = _url;
			this.getModule = getModule;
			this.getInstance = getInstance;
			function getModule() {
				if(module === null) {
					return new Promise((resolve, reject) => {
						fetch(url)
						.then(response => response.arrayBuffer())
						.then(buf => {
							WebAssembly.compile(buf)
							.then(_module => {
								module = _module;
								resolve(_module)
							});
						})
						.catch(reason => reject(reason))
					});
				} else {
					return new Promise((resolve, reject) => resolve(module));
				}
			};
			function getInstance(importObjects) {
				if(module === null) {
					return new Promise((resolve, reject) => {
						getModule()
						.then(_module => {
							if(!!importObjects) resolve(new WebAssembly.Instance(_module, importObjects));
							else  resolve(new WebAssembly.Instance(_module));
						})
						.catch(reason => reject(reason))
					});
				} else {
					return new Promise((resolve, reject) => {
						if(!!importObjects) resolve(new WebAssembly.Instance(module, importObjects));
						else resolve(new WebAssembly.Instance(module));
					});
				}
			}
		}
	})(_fetch);
})(this);

修改的部份,簡單說就是能不改的地方就不改,然後用module.exports是否存在來做判斷是否為node.js的module,然後依照原本使用fetch API的方式,做一個fake給他呼叫。

測試一下

WebAssembly的部份,就拿之前做過的Fibonacci來當例子:

(module
	(func $fibn (param $cur i32) (param $nex i32) (param $n i32) (result i32)
		get_local $n
		i32.const 0
		i32.eq				;;param for if

		if (result i32)
			get_local $cur
		else
			get_local $nex	;;1st param for $fibn
			
			get_local $cur
			get_local $nex
			i32.add			;;2nd param for $fibn

			get_local $n
			i32.const 1
			i32.sub			;;3rd param for fibn

			call $fibn
		end
	)
	(func (export "fib") (param $n i32) (result i32)
		i32.const 0
		i32.const 1
		get_local $n
		call $fibn
	)
)

然後寫一個最簡單的node.js程式來跑跑看:

const Wasm = require('../wasm_util');

new Wasm('test019.wasm').getInstance()
.then(instance => {
	console.log(instance.exports.fib(10));
})

跑完可以看到console中出現55,就表示成功了。


明天來把wasm_util.js放到github,這樣可以用npm透過github網只來引入,使用會比較方便。另外,也試一下更複雜的範例。


上一篇
[練習 17] 目前版本的狀況以及未來展望
下一篇
[練習 19] 總之,就來個結語吧,我沒梗了
系列文
謙虛,踏實的Web Assembly練習20
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言