在Go中使用QJS和WebAssembly运行现代ES2023 JavaScript

小编007 2026-01-01 03:05 7 0
2026-01-01 03:05
第1楼

摘要:Close() ctx := rt.Context() result, err := ctx.Println("Context value:", c.


QJS是一个不依赖CGO的现代JavaScript运行时,用于Go语言,它将QuickJS引擎嵌入到WebAssembly模块中,并使用Wazero运行它,为Go应用程序提供了一个带有async/await和和紧密Go-JS互操作性的沙箱化ES2023环境。

 

QJS的目标是那些希望在Go进程中运行现代JavaScript而不链接本地C库的Go开发人员。它没有直接通过CGO绑定QuickJS,而是将QuickJS-NG编译为WebAssembly,并在Wazero下执行,提供:

完整的ES2023支持(模块、async/await、BigInt等)。一个完全沙箱化、内存安全的执行模型。无需CGO工具链或C运行时依赖。

 

该运行时与Go 1.22+兼容,并作为常规Go模块分发:

 

go get github.com/fastschema/qjs

 

然后:

 

import "github.com/fastschema/qjs"

 

QJS公开了一个 Runtime 和 Context API",允许Go代码评估JavaScript、绑定函数和交换数据结构。一个最简单的示例创建了一个运行时,计算脚本,并将结构化结果读回到Go中:

 

rt, err := qjs.New() if err != nil { log.Fatal(err) } defer rt.Close() ctx := rt.Context() result, err := ctx.Eval("test.js", qjs.Code(` const person = { name: "Alice", age: 30, city: "New York" }; const info = Object.keys(person).map(key => key + ": " + person[key] ).join(", "); ({ person: person, info: info }); )) if err != nil { log.Fatal("Eval error:", err) } defer result.Free() log.Println(result.GetPropertyStr("info").String()) log.Println(result.GetPropertyStr("person").GetPropertyStr("name").String()) log.Println(result.GetPropertyStr("person").GetPropertyStr("age").Int32())

 

Go函数可以暴露给JavaScript,JS函数可以转换回类型化的Go可调用函数。例如,绑定一个Go函数:

 

ctx.SetFunc("goFunction", func(this qjs.This) (qjs.Value, error) { return this.Context().NewString("Hello from Go!"), nil }) result, err := ctx.Eval("test.js", qjs.Code( const message = goFunction(); message; )) if err != nil { log.Fatal("Eval error:", err) } defer result.Free() log.Println(result.String()) // Hello from Go!

 

QJS还支持将更丰富的Go结构体转换为JS值,包括可以从JavaScript调用的方法,然后反序列化为类型化的Go值。

 

为了避免重复序列化大型或不透明的Go对象,QJS引入了Proxy",这是一个轻量级的JavaScript包装器,只保存对Go值的引用。这对于上下文、数据库句柄或JS不需要检查就可以通过的大型结构体来说很有用:

 

ctx.SetFunc("$context", func(this qjs.This) (qjs.Value, error) { passContext := context.WithValue(context.Background(), "key", "value123") val := ctx.NewProxyValue(passContext) return val, nil }) goFuncWithContext := func(c context.Context, num int) int { log.Println("Context value:", c.Value("key")) return num * 2 }

 

JavaScript接收代理并将其传回Go,其中JsValueToGo恢复底层值和类型。QJS通过允许Go异步解决JS承诺来支持async/await。一个Go异步函数可以调度工作并解决一个承诺:

 

ctx.SetAsyncFunc("asyncFunction", func(this *qjs.This) { go func() { time.Sleep(100 * time.Millisecond) result := this.Context().NewString("Async result from Go!") this.Promise().Resolve(result) }() })

 

然后JS await它:

 

async function main() { const result = await asyncFunction(); return result; } ({ main: main() });

 

该运行时可用于在JavaScript中实现HTTP处理程序,同时保持服务器在Go中。例如, /about 和 /contact 路由在JS中定义,预编译为字节码,并从运行时池中执行:

 

byteCode := must(ctx.Compile("script.js", qjs.Code(script), qjs.TypeModule())) pool := qjs.NewPool(3, &qjs.Option{}, func(r *qjs.Runtime) error { results := must(r.Context().Eval("script.js", qjs.Bytecode(byteCode), qjs.TypeModule())) r.Context().Global().SetPropertyStr("handlers", results) return nil }) http.HandleFunc("/about", func(w http.ResponseWriter, r *http.Request) { runtime := must(pool.Get()) defer pool.Put(runtime) handlers := runtime.Context().Global().GetPropertyStr("handlers") result := must(handlers.InvokeJS("about")) fmt.Fprint(w, result.String()) result.Free() })

 

计算阶乘(10)1,000,000次

 

AreWeFastYet V8-V7

 

通过这种设计,QJS的目标是那些需要安全的插件系统、用户提供的脚本或用JavaScript编写的嵌入式业务逻辑,而不需要将C工具链或CGO引入构建和部署流水线的Go开发人员。

 

原文链接:

https://www.infoq.com/news/2025/12/javascript-golang-wasm/"

  • 1 / 1 页
敬请注意:文中内容观点和各种评论不代表本网立场!若有违规侵权,请联系我们.