如何等待来自 golang wasm 的 js 异步函数?
How to wait a js async function from golang wasm?
我写了一个小函数 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 吗?
你不需要 goroutines :D
我在这段代码中发现了一些问题,这些问题并没有使它成为惯用的,并且可能导致一些错误(其中一些可能会锁定您的回调并导致您描述的这种情况):
- 您不应关闭处理函数内的
done
通道。由于并发,这可能会导致意外关闭,这通常是一种不好的做法。
- 由于无法保证执行顺序,更改外部变量可能会导致一些并发问题。最好只使用通道与外部函数通信。
onResolve
和 onCatch
的结果应该使用不同的渠道。这可以更好地处理输出并单独整理主函数的结果,最好通过 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
}
}
这将使该函数成为惯用函数,因为该函数将 return resolve, reject
数据,有点像 Go 中常见的 result, err
情况。也更容易处理不需要的并发,因为我们不处理不同闭包中的变量。
最后但同样重要的是,确保您没有在 Javascript Promise
中同时调用 resolve
和 reject
,因为 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.
我写了一个小函数 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 吗?
你不需要 goroutines :D
我在这段代码中发现了一些问题,这些问题并没有使它成为惯用的,并且可能导致一些错误(其中一些可能会锁定您的回调并导致您描述的这种情况):
- 您不应关闭处理函数内的
done
通道。由于并发,这可能会导致意外关闭,这通常是一种不好的做法。 - 由于无法保证执行顺序,更改外部变量可能会导致一些并发问题。最好只使用通道与外部函数通信。
onResolve
和onCatch
的结果应该使用不同的渠道。这可以更好地处理输出并单独整理主函数的结果,最好通过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
}
}
这将使该函数成为惯用函数,因为该函数将 return resolve, reject
数据,有点像 Go 中常见的 result, err
情况。也更容易处理不需要的并发,因为我们不处理不同闭包中的变量。
最后但同样重要的是,确保您没有在 Javascript Promise
中同时调用 resolve
和 reject
,因为 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.