WebAssembly入門筆記[4]:利用Global傳遞全局變量
利用WebAssembly的導(dǎo)入導(dǎo)出功能可以靈活地實現(xiàn)宿主JavaScript程序與加載的單個wasm模塊之間的交互,那么如何在宿主程序與多個wasm之間傳遞和共享數(shù)據(jù)呢?這就需要使用到Global這個重要的對象了。
一、數(shù)值類型全局變量
二、將JavaScript函數(shù)設(shè)置為全局變量
三、利用全局變量處理字符串
一、數(shù)值類型全局變量
Global全局變量支持多種值類型,包括數(shù)組(i32/i64和f32/f64)、向量和引用類型(externref和funcref)。下面的實例利用Global提供了全局計數(shù)的功能。在WebAssembly Text Format (WAT)文件app.wat中,我們從宿主JavaScript應(yīng)用中導(dǎo)入了一個i32類型的可讀寫(mut表示可以修改)的全局變量,導(dǎo)入路徑為“imports.counter”,我們將其命名為$counter。在用于自增的導(dǎo)出函數(shù)increment中,我們通過執(zhí)行g(shù)lobal.get指令讀取全局變量的值,并將其加1之后,執(zhí)行g(shù)lobal.set指令對全局變量重新賦值。
(module (global $counter (import "imports" "counter") (mut i32)) (func (export "increment") (i32.add (global.get $counter) (i32.const 1)) (global.set $counter) ) )
在index.html文件中,我們在頁面中添加了一個“Increment”按鈕,并利用一個<span>顯式計算器當(dāng)前的值。JavaScript腳本通過調(diào)用WebAssembly.Global構(gòu)造函數(shù)將代表全局變量的Global對象創(chuàng)建出來后,調(diào)用WebAssembly.instantiateStreaming加載app.wat編譯生成的app.wasm模塊文件,并將此Global對象包含在導(dǎo)入對象中。
<html>
<head></head>
<body>
<span id="counter">0</span>
<button id="btnInc">Increment</button>
<script>
const globalCounter = new WebAssembly.Global({ value: "i32", mutable: true }, 0);
WebAssembly
.instantiateStreaming(fetch("app.wasm"), {"imports":{"counter":globalCounter}})
.then(results => {
document.getElementById("btnInc").onclick = ()=>{
results.instance.exports.increment();
document.getElementById("counter").innerText = globalCounter.value;
};
});
</script>
</body>
</html>wasm模塊充成功導(dǎo)入后,我們注冊了按鈕的click事件,使之在調(diào)用導(dǎo)出的increment函數(shù)后,重新刷新計數(shù)器的值。如下圖所示,針對“Increment”的每次點擊都將計數(shù)器加1(源代碼)。
二、將JavaScript函數(shù)設(shè)置為全局變量
除了四種數(shù)值類型,Global還支持兩種引用類型externref和funcref,利用externref可以將宿主應(yīng)用提供的任意JavaScript對象作為全局變量,下面的實例演示利用這種方式實現(xiàn)了與類似的功能。如下面的代碼片段所示,新的app.wat導(dǎo)入了一個類型為externref的全局變量,對應(yīng)著數(shù)組應(yīng)用提供的一個用來對全局計數(shù)自增的Javascript函數(shù)。
(module (func $apply (import "imports" "apply") (param externref)) (global $increment (import "imports" "increment") externref) (func $main (call $apply (global.get $increment)) ) (start $main) )
由于JavaScript函數(shù)的引用在.wasm模塊中并不能直接執(zhí)行,所以我們不得不導(dǎo)入一個apply函數(shù)“回傳”到宿主應(yīng)用中執(zhí)行。我們修改的應(yīng)用用來統(tǒng)計導(dǎo)入的wasm模塊的數(shù)量,所以我們在入口函數(shù)$main中利用apply調(diào)用了全局變量$increment引用的函數(shù)。
在index.html,我們在頁面中添加了一個“Load”按鈕來加載app.wat編譯生成的app.wasm模塊。JavaScript腳本利用counter變量表示加載的wasm模塊數(shù)量,并通過調(diào)用WebAssembly.Global構(gòu)造函數(shù)創(chuàng)建了rexternref類型的全局變量,其值為一個對counter自增的函數(shù)。
<html> <head></head> <body> <p>There are totally <span id="counter" style= "color: read”>0</span> wasm modules loaded. </p> <button id="btnLoad">Load</button> <script> var counter = 0; const globalIncrement = new WebAssembly.Global({ value: "externref"}, ()=>counter++); var apply = func => func(); document.getElementById("btnLoad").onclick = ()=>{ WebAssembly .instantiateStreaming(fetch("app.wasm"), {"imports":{"increment":globalIncrement,"apply": apply }}) .then(_=>{ document.getElementById("counter").innerText = counter; }) }; </script> </body> </html>
我們將這個Global對象包含到導(dǎo)入的對象中,并在導(dǎo)入成功后刷新顯式的計數(shù)器,所以程序運行后將會顯式當(dāng)前加載的wasm模塊數(shù)量(源代碼)。
三、利用全局變量處理字符串
WebAssembly目前并沒有提供針對字符串類型的直接支持,而是單純地將其作為字節(jié)序列看到。目前字符串在宿主程序與wasm模塊之間的傳遞只有通過Memory來實現(xiàn)。由于Javascript具有處理字符串的能力,wasm模塊可以將字符串作為externref回傳到宿主程序進行處理。在接下來演示的程序中,我們在app.wat中定義一個“字符類型(實際上是externref類型)”的全局變量,導(dǎo)出的greet函數(shù)通過調(diào)用導(dǎo)入的print函數(shù)將其輸出。
(module (func $print (import "imports" "print") (param externref)) (global $message (import "imports" "message") (mut externref)) (func (export "greet") (call $print (global.get $message)) ) )
在index.html中,我們在頁面上放置了三個按鈕,讓它們在命名為“greet”的<div>中分別顯示“Good Morning”、“Good Afternoon”和“Good Evening”三條問候語。具體的問候語通過函數(shù)print輸出,它的參數(shù)就是代表輸出文本的字符串。
<html>
<head></head>
<body>
<div id="greet"></div>
<button id="btnMorning">Morning</button>
<button id="btnAfternoon">Afternoon</button>
<button id="btnEvening">Evening</button>
<script>
var print = (msg) => {
console.log(msg);
document.getElementById("greet").innerText = msg;
}
const globalMsg = new WebAssembly.Global({ value: "externref", mutable: true }, "Good Morning!");
console.log(globalMsg.value);
WebAssembly
.instantiateStreaming(fetch("app.wasm"), {"imports":{"message":globalMsg, "print":print}})
.then(results => {
var greet = results.instance.exports.greet;
console.log(greet);
document.getElementById("btnMorning").onclick = ()=>{
globalMsg.value = "Good Morning!";
greet();
};
document.getElementById("btnAfternoon").onclick = ()=>{
globalMsg.value = "Good Afternoon!";
greet();
};
document.getElementById("btnEvening").onclick = ()=>{
globalMsg.value = "Good Evening!";
greet();
};
});
</script>
</body>
</html>我們定義了一個externref類型的Global對象來引用帶輸出的問候語文本,并在加載app.wasm木塊使將其包含到導(dǎo)入對象中。三個按鈕的click事件處理程序通過調(diào)用導(dǎo)出的greet函數(shù)輸出對于的問候語,但是在調(diào)用此函數(shù)之前會對Global對象進行相應(yīng)的賦值(源代碼)。
WebAssembly入門筆記[1]:與JavaScript的交互
WebAssembly入門筆記[2]:利用Memory傳遞字節(jié)數(shù)據(jù)
WebAssembly入門筆記[3]:利用Table傳遞引用
WebAssembly入門筆記[4]:利用Global傳遞全局變量
WebAssembly核心編程[1]:wasm模塊實例化的N種方式
WebAssembly核心編程[2]:類型系統(tǒng)
WebAssembly核心編程[3]: Module 與 Instance
WebAssembly核心編程[4]: Memory


利用WebAssembly的導(dǎo)入導(dǎo)出功能可以靈活地實現(xiàn)宿主JavaScript程序與加載的單個wasm模塊之間的交互,那么如何在宿主程序與多個wasm之間傳遞和共享數(shù)據(jù)呢?這就需要使用到Global這個重要的對象了。



浙公網(wǎng)安備 33010602011771號