在 我以前的文章 中,我们探讨了创建一个没有Emscripten管道的**WebAssembly模块的想法。在那篇文章中,我们能够从JavaScript中调用模块,但我们没有进入到反向。
如何从一个模块调用到JavaScript?
在这篇文章中,我们将尝试让WebAssembly模块调用我们JavaScript中定义的方法。
为了编译C代码,当使用的方法不在源代码中时,你需要用extern关键字定义方法的签名,如下面的例子所示。
// Define the JavaScript method's signature that we're going to be calling.
extern void CallJS(int iVal);
// A method that the JavaScript will call into to trigger our code
// that will in turn call a JavaScript method passing along the value
// received.
void Test(int iVal){ CallJS(iVal); }
你可以运行以下命令行来构建wasm文件。
emcc test.c -s WASM=1 -s SIDE_MODULE=1 -O1 -o test.wasm
为了在我们的JavaScript中定义这个方法,我们只需要把它添加到env对象中,这个对象是我们作为第2个参数传递给 WebAssembly.instantiate
方法的main对象的一部分。
var importObject = {
'env': {
// ... (other properties/methods of this object)
'_CallJS': function(iVal){ alert("value received: " + iVal.toString()); }
}
};
就像我们在从JavaScript中调用模块时需要在方法名前添加下划线一样,我们在这里也需要在方法名前添加下划线。
以下是我们的例子模块的完整HTML/JavaScript。
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8"/>
</head>
<body>
<input type="text" id="txtValue" />
<input type="button" value="Pass Value" onclick="PassValue();" />
<script type="text/javascript">
var gModule = null;
var importObject = {
'env': {
'memoryBase': 0,
'tableBase': 0,
'memory': new WebAssembly.Memory({initial: 256}),
'table': new WebAssembly.Table({initial: 0, element: 'anyfunc'}),
'_CallJS': function(iVal){ alert("value received: " + iVal.toString()); }
}
};
fetch('test.wasm').then(response =>
response.arrayBuffer()
).then(bytes =>
WebAssembly.instantiate(bytes, importObject)
).then(results => {
gModule = results.instance; // Hold onto the module's instance so that we can reuse it
});
function PassValue(){
// Get the value from the textbox (convert the value from a string to an int)
var iVal = parseInt(document.getElementById("txtValue").value,10);
// Call the method in the module
gModule.exports._Test(iVal);
}
</script>
</body>
</html>