如何从 Go web assembly 中 "throw" JS 错误?

How to "throw" JS error from Go web assembly?

我试过了

js.Global().Call("throw", "yeet")

但回来了

panic: 1:1: expected operand, found 'type' [recovered] wasm_exec.f7bab17184626fa7f3ebe6c157d4026825842d39bfae444ef945e60ec7d3b0f1.js:51 panic: syscall/js: Value.Call: property throw is not a function, got undefined

我看到在 syscall/js 中定义了一个 Error 类型,但是没有关于抛出它的任何内容 https://golang.org/pkg/syscall/js/#Error

不可能 throw 来自 WebAssembly 的 JS 错误。在 JS 中,当您 throw 一个值时,JS 运行时会将堆栈展开到最近的 try 块,或者记录一个未捕获的错误。 WASM 执行是在无法直接访问 JS 堆栈的单独执行环境中的隔离沙箱中执行的。来自 WASM docs:

Each WebAssembly module executes within a sandboxed environment separated from the host runtime using fault isolation techniques.

如果 WASM 调用抛出的 JS 代码,错误将被 WASM 运行时捕获并处理,就好像 WASM 代码已经崩溃一样。 WASM 可以访问 traps,但它们旨在在运行时级别立即停止执行(并且未在 Go 的 syscall/js 模块中实现)。

表示可能失败的代码执行的惯用方法是 return Promise,然后 resolve 承诺成功或 reject 失败。调用 JS 代码可以在 try/catch 块中等待 promise 执行并在那里处理错误,或者使用 promise 链并在 .catch() 回调中处理错误。这是一个简短的例子:

func main() {
    c := make(chan struct{})

    js.Global().Set("doSomething", js.FuncOf(func(this js.Value, args []js.Value) interface{} {
        handler := js.FuncOf(func(this js.Value, args []js.Value) interface{} {
            resolve := args[0]
            reject := args[1]

            go func() {
                data, err := doSomeWork()
                if err != nil {
                    // err should be an instance of `error`, eg `errors.New("some error")`
                    errorConstructor := js.Global().Get("Error")
                    errorObject := errorConstructor.New(err.Error())
                    reject.Invoke(errorObject)
                } else {
                    resolve.Invoke(js.ValueOf(data))
                }
            }()

            return nil
        })

        promiseConstructor := js.Global().Get("Promise")
        return promiseConstructor.New(handler)
    })

    <-c
}

然后,在你的 JS 代码中:

(async () => {
  try {
    await window.doSomething();
  } catch (err) {
    console.log('caught error from WASM:', err);
  }
}();

window.doSomething()
  .then(_ => /* ... */)
  .catch(err => {
    console.log('caught error from WASM:', err);
  });

我不同意@superhawk610

It's not possible to throw a JS error from WebAssembly

可以扔,但实用性如何?根据浏览器的不同,您甚至可能获得一些可读的堆栈跟踪,但大多数情况下有一些 Promise 逻辑,如 @superhawk610 解释会更有意义。但是,如果你真的不想抛出异常,这里是一个简单的例子。

例子

1.你可以提供 throw 函数 stub

// Throw function stub to throw javascript exceptions
// Without func body!
func Throw(exception string, message string)

2。然后通过创建 yourpkg_js.s 文件

为汇编程序提供提示
// Throw enables to throw javascript exceptions
TEXT ·Throw(SB), NOSPLIT, [=11=]
  CallImport
  RET

3。通过扩展 wasm_exec / 你的 wasm importObject

添加 js 回调
this.importObject = {
  go: {
    // ...

    // func Throw(exception string, message string)
    '<your-pkg-import-path>.Throw': (sp) => {
      const exception = loadString(sp + 8)
      const message = loadString(sp + 24)
      const throwable = globalThis[exception](message)
      throw throwable
    }
  }
}

然后您可以通过提供 Error class 名称和消息来抛出错误。例如

func main () {
  Throw("TypeError", "invalid arguments")
}