等待 JavaScript 中的异步函数(await)在 Go 函数内部执行(async)。

huangapple go评论85阅读模式
英文:

await js async function (promise) inside a go function

问题

我正在寻找将IndexDB集成到基于WASM的应用程序中。在Go函数中,如何"await"来自JavaScript函数的Promise。以下是示例:

func Get(key string) []byte {
    found := js.Global().Get("Store").Call("getItem", key)
    // 等待 found
    // 将 js.Value 转换为 []byte
    return nil
}

异步回调也可以。

附注:一个不好的解决方案是创建一个带有无限循环的Go协程,等待直到DOM变量存在,例如global.solution+ID被设置。但我认为这是一个不好的解决方案。

英文:

I am looking to integrate indexdb in a wasm based app. How do you "await" in a go function a promise from a js function. Here is the example

    async getItem(key) {
        try{
            const out = await database.getItem(key);
            return out;
        }catch(err){
            return null;
        }
    }

and in go

func Get(key string)[]byte{

	found :=  js.Global().Get("Store").Call('getItem', key )
    // await for found
    // convert js.Value to to []byte
	return nil

}

Async callbacks are fine too.

LE: one bad solution would be to create a go routine with an infinite loop waiting until a DOM variable exists like global.solution+ID to be set. But I believe this is a bad solution

答案1

得分: 1

你可以使用Promise对象的then方法来等待结果,就像这样:

package main

import (
	"fmt"
	"syscall/js"
)

func main() {
	wait := make(chan interface{})
	js.Global().Call("sayHello", 5000).Call("then", js.FuncOf(func(this js.Value, args []js.Value) interface{} {
		fmt.Println(args[0])
		wait <- nil
		return nil
	}))
	<-wait
	fmt.Println("we're done here")
}

请注意,我们在Go代码中使用了一个通道来实际等待。我们需要这样做是因为Go程序在接收到来自JavaScript的回调时仍然必须保持运行状态。

index.html文件:

<html>
	<head>
		<meta charset="utf-8"/>
		<script src="wasm_exec.js"></script>
		<script>
			const go = new Go();
			WebAssembly.instantiateStreaming(fetch("main.wasm"), go.importObject).then((result) => {
				go.run(result.instance);
			});

			function sayHello(time) {
				return new Promise(resolve => {
					console.log('waiting %dms and resolving', time)
					setTimeout(() => resolve('hola!'), time)
				})
			}
		</script>
	</head>
	<body></body>
</html>
英文:

You can use the then method from the Promise object to wait for the result, something like this:

package main

import (
	&quot;fmt&quot;
	&quot;syscall/js&quot;
)

func main() {
	wait := make(chan interface{})
	js.Global().Call(&quot;sayHello&quot;, 5000).Call(&quot;then&quot;, js.FuncOf(func(this js.Value, args []js.Value) interface{} {
		fmt.Println(args[0])
		wait &lt;- nil
		return nil
	}))
	&lt;-wait
	fmt.Println(&quot;we&#39;re done here&quot;)
}

Notice that we are using a channel to actually wait in the Go code. We need to do that because the Go program must still be running while receiving the callback from Javascript.

The index.html file:

&lt;html&gt;
	&lt;head&gt;
		&lt;meta charset=&quot;utf-8&quot;/&gt;
		&lt;script src=&quot;wasm_exec.js&quot;&gt;&lt;/script&gt;
		&lt;script&gt;
			const go = new Go();
			WebAssembly.instantiateStreaming(fetch(&quot;main.wasm&quot;), go.importObject).then((result) =&gt; {
				go.run(result.instance);
			});

			function sayHello(time) {
				return new Promise(resolve =&gt; {
					console.log(&#39;waiting %dms and resolving&#39;, time)
					setTimeout(() =&gt; resolve(&#39;hola!&#39;), time)
				})
			}
		&lt;/script&gt;
	&lt;/head&gt;
	&lt;body&gt;&lt;/body&gt;
&lt;/html&gt;

答案2

得分: 1

承诺,真实示例(go.1.18):

type DevicesData struct {
	DeviceId string
	GroupId  string
	Kind     string
	Label    string
}

list := make([]DevicesData, 0)
end := make(chan struct{})

// golang存在一个bug:
// enumerateDevices()返回一个数组,但是go返回一个对象。
forEach := js.FuncOf(func(_ js.Value, args []js.Value) interface{} {
	data := DevicesData{
		DeviceId: args[0].Get("deviceId").String(),
		GroupId:  args[0].Get("groupId").String(),
		Kind:     args[0].Get("kind").String(),
		Label:    args[0].Get("label").String(),
	}
	list = append(list, data)

	// 总是返回nil
	return nil
})

// promise成功函数
success := js.FuncOf(func(_ js.Value, args []js.Value) interface{} {
	args[0].Call("forEach", forEach)
	end <- struct{}{}

	// 总是返回nil
	return nil
})

failure := js.FuncOf(func(this js.Value, args []js.Value) interface{} {
	err := errors.New(args[0].Get("message").String())

	// 总是返回nil
	return nil
})

js.Global().Get("navigator").Get("mediaDevices").Call("enumerateDevices").Call("then", success, failure)

// 等待异步调用
<-end

这是一个使用Promise的真实示例,用于在Go语言中调用JavaScript代码。它演示了如何使用Go调用JavaScript的navigator.mediaDevices.enumerateDevices()方法,并处理成功和失败的情况。在成功回调函数中,它使用forEach方法遍历返回的设备列表,并将每个设备的信息存储在Go语言的DevicesData结构中。在失败回调函数中,它创建一个错误对象并进行处理。最后,通过等待end通道来等待异步调用的完成。

英文:

Promise, real example (go.1.18):

type DevicesData struct {
	DeviceId string
	GroupId string
	Kind string
	Label string
}

list = make([]DevicesData, 0)
end := make(chan struct{})

// golang has a bug:
// enumerateDevices() returns an array, but, go returns an object.
forEach := js.FuncOf(func(_ js.Value, args []js.Value) any {
	data := DevicesData{
		DeviceId: args[0].Get(&quot;deviceId&quot;).String(),
		GroupId:  args[0].Get(&quot;groupId&quot;).String(),
		Kind:     args[0].Get(&quot;kind&quot;).String(),
		Label:    args[0].Get(&quot;label&quot;).String(),
	}
	list = append(list, data)

	// aways return nil
	return nil
})

// promise success function
var success = js.FuncOf(func(_ js.Value, args []js.Value) interface{} {
	args[0].Call(&quot;forEach&quot;, forEach)
	end &lt;- struct{}{}

	// aways return nil
	return nil
})

var failure = js.FuncOf(func(this js.Value, args []js.Value) interface{} {
	err = errors.New(args[0].Get(&quot;message&quot;).String())

	// aways return nil
	return nil
})

js.Global().Get(&quot;navigator&quot;).Get(&quot;mediaDevices&quot;).Call(&quot;enumerateDevices&quot;).Call(&quot;then&quot;, success, failure)

// wait async call
&lt;-end

huangapple
  • 本文由 发表于 2021年7月17日 06:02:23
  • 转载请务必保留本文链接:https://go.coder-hub.com/68415674.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定