Wasm 代码如何在浏览器中运行以及如何与 JavaScript 交互

问题描述 投票:0回答:1

我很好奇 WebAssembly (Wasm) 代码如何在浏览器中运行。我知道浏览器中的 JavaScript 代码是由 JavaScript 引擎执行的,但我不确定谁执行 Wasm 二进制文件。它也是 JavaScript 引擎吗?

此外,我想了解异步 JavaScript 函数如何与 Wasm 交互。当我们通过 JavaScript 加载 Wasm 二进制文件并调用导出的 Wasm 函数时,它会添加到 JavaScript 调用堆栈中。但是如果 Wasm 函数在其体内调用异步 JavaScript 函数会发生什么?异步函数的回调是否添加到JS队列堆栈中? JS 和 Wasm 共享相同的调用堆栈和事件队列,还是各有自己的调用堆栈和事件队列?如果他们各自有自己的栈和队列,他们如何协调执行?

假设在 Wasm 模块中我们导入 JS 异步函数并在我们的 wasm 函数之一中调用它

(import "env" "async_js"
(func $async_js (param i32) (result i32))) 
    
(func $fnc (result i32)   
  (call $async_js (i32.const 1))
  ;;  other code here  
)

现在在 js 中,当 wasm 模块加载时,我们调用 fnc 函数

wasm.instance.exports.fnc()

那么,JS 异步函数被调用,JS 异步函数的回调被添加到 js 队列堆栈中,并且执行 wasm fnc 函数的其余部分,对吧?但是浏览器如何知道从哪里继续或者如何在 wasm 和 js 之间切换?

javascript webassembly callstack
1个回答
0
投票

WebAssembly 目前不支持等待承诺,因此您提供的代码将无法按您的预期工作。

异步Javascript函数总是返回一个

Promise
对象。您导入的声明为
(func $async_js (param i32) (result i32))
的“异步”函数具有
i32
返回类型,这意味着它不是异步的。

WebAssembly 不是等待函数调用,实际发生的是返回的 Javascript Promise 对象在传递给 WebAssembly 时被转换为整数(恰好为 0),并且 WebAssembly 函数立即返回。需要明确的是,被调用的异步函数仍然会执行,并且它的回调会像平常一样添加到队列堆栈中,但它不会“继续”到 wasm 代码中,因为它从未在那里“等待”。下面的代码片段显示了这种行为:

void async function(){
  "use strict";
  // `binary` is the compiled version of the following WAT code:
  // (module
  //   (import "env" "asyncJs" (func $async_js (param i32) (result i32)))
  //   (func $fnc (export "fnc") (result i32)   
  //     i32.const 1
  //     call $async_js
  //   )
  // )
  const binary = new Uint32Array([1836278016,1,1610746369,2130804481,2130772064,50401026,125202021,1853453153,7555683,16909056,17237761,1668179459,134873344,1090520577,184553473,1845763584,23424353,134218256,1853453153,1936351075,1852179201,33882723,65536]).buffer;
  const module = new WebAssembly.Module(binary);
  const instance = new WebAssembly.Instance(module, {
    "env": {
      "asyncJs": async function(){
        // Wait for a second
        await new Promise(resolve => window.setTimeout(resolve, 1000));
        document.body.append(document.createTextNode(`Awaited timeout.`));
        return 1;
      }
    }
  });
  const start = new Date;
  const result = await instance.exports.fnc(); // The await here does nothing because WebAssembly functions are never async!
  const duration = new Date - start;
  document.body.append(document.createTextNode(`Result: ${result}, which took ${duration}ms.`));
}();

您应该看到 WebAssembly 函数如何立即返回 0,并且异步函数如何在异步任务解析后打印一条消息(在本例中为一秒计时器)。异步函数 (1) 的返回值没有在任何地方使用。

Javascript Promise 集成 WebAssembly 提案旨在添加功能,导入的异步函数可以“挂起”WebAssembly 堆栈,直到被调用的函数解析(或拒绝)promise。然而,该提案尚未被任何浏览器或 WebAssembly 运行时完全实现,并且默认情况下不会在任何浏览器中发布。

目前,从 WebAssembly 成功获取 Promise 值的唯一方法是完全从当前执行堆栈返回,并在 Promise 解析后接收来自 Javascript 的回调。

要回答您的其他问题,重要的是要注意所有 WebAssembly 函数都可以像任何其他 Javascript 函数一样对待:它们可以被调用,它们可以调用其他 Javascript(或 WebAssembly)函数,并且它们可以返回一个值。无法确定函数是执行 Javascript 还是 WebAssembly 代码。

实际上,这意味着当调用 WebAssembly 函数时,它会像平常一样添加到调用堆栈中,并且没有理由拥有单独的堆栈,因为所有 WebAssembly 代码都是同步运行的。因此,您可以认为 Javascript 引擎同时执行 Javascript 和 WebAssembly 代码,即使实现可能选择以与 Javascript 代码不同的方式编译和执行 WebAssembly 代码。

© www.soinside.com 2019 - 2024. All rights reserved.