首页 > 解决方案 > 如何等待来自 golang wasm 的 js 异步函数?

问题描述

我写了一个小函数await来处理 go 中的异步 javascript 函数:

func await(awaitable js.Value) (ret js.Value, ok bool) {
    if awaitable.Type() != js.TypeObject || awaitable.Get("then").Type() != js.TypeFunction {
        return awaitable, true
    }
    done := make(chan struct{})


    onResolve := js.FuncOf(func(this js.Value, args []js.Value) interface{} {
        glg.Info("resolve")
        ret = args[0]
        ok = true
        close(done)
        return nil
    })
    defer onResolve.Release()

    onReject := js.FuncOf(func(this js.Value, args []js.Value) interface{} {
        glg.Info("reject")
        ret = args[0]
        ok = false
        close(done)
        return nil
    })
    defer onReject.Release()

    onCatch := js.FuncOf(func(this js.Value, args []js.Value) interface{} {
        glg.Info("catch")
        ret = args[0]
        ok = false
        close(done)
        return nil
    })
    defer onCatch.Release()


    glg.Info("before await")
    awaitable.Call("then", onResolve, onReject).Call("catch", onCatch)
    // i also tried go func() {awaitable.Call("then", onResolve, onReject).Call("catch", onCatch)}()
    glg.Info("after await")
    <-done
    glg.Info("I never reach the end")
    return
}

问题是,当我使用或不使用 goroutine 调用函数时,事件处理程序似乎被阻塞,我被迫重新加载页面。我从来没有遇到过任何回调,我的频道也从来没有关闭过。有没有什么惯用的方法可以在 wasm 中调用来自 Golang 的承诺的 await ?

标签: javascriptgowebassemblylanguage-interoperability

解决方案


你不需要那个goroutines:D

我在这段代码中看到了一些问题,这些问题并不符合习惯并且可能导致一些错误(其中一些可能会锁定你的回调并导致你描述的这种情况):

  • 您不应该关闭done处理函数内的通道。由于并发,这可能会导致不必要的关闭,这通常是一种不好的做法。
  • 由于无法保证执行顺序,因此更改外部变量可能会导致一些并发问题。最好只使用通道与外部函数进行通信。
  • 结果来自onResolve并且onCatch应该使用不同的渠道。这可以导致更好地处理输出并单独整理 main 函数的结果,最好是通过select语句。
  • 不需要单独的onRejectandonCatch方法,因为它们相互重叠的职责。

如果我必须设计此await功能,我会将其简化为如下所示:

func await(awaitable js.Value) ([]js.Value, []js.Value) {
    then := make(chan []js.Value)
    defer close(then)
    thenFunc := js.FuncOf(func(this js.Value, args []js.Value) interface{} {
        then <- args
        return nil
    })
    defer thenFunc.Release()

    catch := make(chan []js.Value)
    defer close(catch)
    catchFunc := js.FuncOf(func(this js.Value, args []js.Value) interface{} {
        catch <- args
        return nil
    })
    defer catchFunc.Release()

    awaitable.Call("then", thenFunc).Call("catch", catchFunc)

    select {
    case result := <-then:
        return result, nil
    case err := <-catch:
        return nil, err
    }
}

这将使函数具有惯用性,因为函数会返回resolve, reject数据,有点像result, errGo 中常见的情况。由于我们不处理不同闭包中的变量,因此处理不需要的并发也更容易一些。

最后但并非最不重要的一点是,确保您没有同时调用JavascriptresolverejectJavascript Promise,因为其中的Release方法js.Func明确告诉您一旦释放这些资源就不应访问它们:

// Release frees up resources allocated for the function.
// The function must not be invoked after calling Release.
// It is allowed to call Release while the function is still running.

推荐阅读