如何从Golang的Wasm中等待JS异步函数?

4

我写了一个小函数 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 中,有没有惯用的方法来调用 Promise 上的 await ?
1个回答

5
这个不需要使用goroutine:D
我在这段代码中看到了几个问题,这些问题使它不够惯用,并可能导致一些错误(其中一些错误可能会锁定回调函数并导致您所描述的情况):
- 你不应该在处理函数内关闭done channel。这可能会由于并发而意外关闭channel,在一般情况下也是一种不好的做法。 - 由于执行顺序无法保证,更改外部变量可能会导致一些并发问题。最好只使用channel与外部函数通信。 - onResolve和onCatch的结果应该使用独立的channel。这可以更好地处理输出并单独为主函数排序结果,理想情况下通过一个select语句来完成。 - 没有必要使用单独的onReject和onCatch方法,因为它们重叠彼此的职责。
如果我设计这个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 数据,有点像 Go 中常见的 result, err 情况。同时,这也更容易处理不需要的并发,因为我们不会在不同闭包中处理变量。
最后,请确保在您的 Javascript Promise 中不要同时调用 resolvereject,因为在 js.Func 中,Release 方法明确告诉您一旦释放这些资源,就不应再访问它们。
// 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.

2
非常感謝,這真的很有效!我的應用程式一直被阻擋,因為我在調用 await 之前使用了 fetch。而現在通過使用 goroutine,我可以使用您的函數了! - roman Sztergbaum

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接