package main
import (
"fmt""log""net/http""github.com/fastschema/qjs"
)
funcmust[Tany](valT, errerror) T {
iferr!=nil {
log.Fatalf("Error: %v", err)
}
returnval
}
constscript=`// JS handlers for HTTP routesconst about = () => { return "QuickJS in Go - Hello World!";};const contact = () => { return "Contact us at [email protected]";};export default { about, contact };`funcmain() {
rt:=must(qjs.New())
deferrt.Close()
ctx:=rt.Context()
// Precompile the script to bytecodebyteCode:=must(ctx.Compile("script.js", qjs.Code(script), qjs.TypeModule()))
// Use a pool of runtimes for concurrent requestspool:=qjs.NewPool(3, &qjs.Option{}, func(r*qjs.Runtime) error {
results:=must(r.Context().Eval("script.js", qjs.Bytecode(byteCode), qjs.TypeModule()))
// Store the exported functions in the global object for easy accessr.Context().Global().SetPropertyStr("handlers", results)
returnnil
})
// Register HTTP handlers based on JS functionsval:=must(ctx.Eval("script.js", qjs.Bytecode(byteCode), qjs.TypeModule()))
methodNames:=must(val.GetOwnPropertyNames())
val.Free()
for_, methodName:=rangemethodNames {
http.HandleFunc("/"+methodName, func(w http.ResponseWriter, r*http.Request) {
runtime:=must(pool.Get())
deferpool.Put(runtime)
// Call the corresponding JS functionhandlers:=runtime.Context().Global().GetPropertyStr("handlers")
result:=must(handlers.InvokeJS(methodName))
fmt.Fprint(w, result.String())
result.Free()
})
}
http.HandleFunc("/", func(w http.ResponseWriter, r*http.Request) {
fmt.Fprintf(w, "Hello from Go's HTTP server!")
})
log.Println("Server listening on :8080")
iferr:=http.ListenAndServe(":8080", nil); err!=nil {
log.Fatalf("Server error: %v\n", err)
}
}
Awaiting a promise
ctx.SetAsyncFunc("asyncFunction", func(this*qjs.This) {
gofunc() {
time.Sleep(100*time.Millisecond)
result:=this.Context().NewString("Async result from Go!")
this.Promise().Resolve(result)
}()
})
result, err:=ctx.Eval("test.js", qjs.Code(`async function main() { const result = await asyncFunction(); return result;}({ main: main() });`))
iferr!=nil {
log.Fatal("Eval error:", err)
}
deferresult.Free()
mainFunc:=result.GetPropertyStr("main")
// Wait for the promise to resolveval, err:=mainFunc.Await()
iferr!=nil {
log.Fatal("Await error:", err)
}
// Output: Async result from Go!log.Println("Awaited value:", val.String())
Top level await
// asyncFunction is already defined aboveresult, err:=ctx.Eval("test.js", qjs.Code(` async function main() { const result = await asyncFunction(); return result; } await main()`), qjs.FlagAsync())
iferr!=nil {
log.Fatal("Eval error:", err)
}
deferresult.Free()
log.Println(result.String())
// Call JS function from Goresult, err:=ctx.Eval("test.js", qjs.Code(` function add(a, b) { return a + b; } function errorFunc() { throw new Error("test error"); } ({ addFunc: add, errorFunc: errorFunc });`))
iferr!=nil {
log.Fatal("Eval error:", err)
}
deferresult.Free()
jsAddFunc:=result.GetPropertyStr("addFunc")
deferjsAddFunc.Free()
goAddFunc, err:= qjs.JsFuncToGo[func(int, int) (int, error)](jsAddFunc)
iferr!=nil {
log.Fatal("Func conversion error:", err)
}
total, err:=goAddFunc(1, 2)
iferr!=nil {
log.Fatal("Func execution error:", err)
}
// Output: 3log.Println("Addition result:", total)
jsErrorFunc:=result.GetPropertyStr("errorFunc")
deferjsErrorFunc.Free()
goErrorFunc, err:= qjs.JsFuncToGo[func() (any, error)](jsErrorFunc)
iferr!=nil {
log.Fatal("Func conversion error:", err)
}
_, err=goErrorFunc()
iferr!=nil {
// Output:// JS function execution failed: Error: test error// at errorFunc (test.js:7:13)log.Println(err.Error())
}
// Load a utility moduleif_, err=ctx.Load("math-utils.js", qjs.Code(` export function add(a, b) { return a + b; } export function multiply(a, b) { return a * b; } export function power(base, exponent) { return Math.pow(base, exponent); } export const PI = 3.14159; export const E = 2.71828; export default { add, multiply, power, PI, E };`)); err!=nil {
log.Fatal("Module load error:", err)
}
// Use the moduleresult, err:=ctx.Eval("use-math.js", qjs.Code(` import mathUtils, { add, multiply, power, PI } from 'math-utils.js'; const calculations = { addition: add(10, 20), multiplication: multiply(6, 7), power: power(2, 8), circleArea: PI * power(5, 2), defaultAdd: mathUtils.add(10, 20) }; export default calculations;`), qjs.TypeModule())
iferr!=nil {
log.Fatal("Module eval error:", err)
}
// Output:// Addition: 30// Multiplication: 42// Power: 256// Circle Area: 78.54// Default Add: 30fmt.Printf("Addition: %d\n", result.GetPropertyStr("addition").Int32())
fmt.Printf("Multiplication: %.0f\n", result.GetPropertyStr("multiplication").Float64())
fmt.Printf("Power: %.0f\n", result.GetPropertyStr("power").Float64())
fmt.Printf("Circle Area: %.2f\n", result.GetPropertyStr("circleArea").Float64())
fmt.Printf("Default Add: %.d\n", result.GetPropertyStr("defaultAdd").Int32())
result.Free()
script:=` function fibonacci(n) { if (n <= 1) return n; return fibonacci(n - 1) + fibonacci(n - 2); } function factorial(n) { return n <= 1 ? 1 : n * factorial(n - 1); } const result = { fib10: fibonacci(10), fact5: factorial(5), timestamp: Date.now() }; result;`// Compile the script to bytecodebytecode, err:=ctx.Compile("math-functions.js", qjs.Code(script))
iferr!=nil {
log.Fatal("Compilation error:", err)
}
fmt.Printf("Bytecode size: %d bytes\n", len(bytecode))
// Execute the compiled bytecoderesult, err:=ctx.Eval("compiled-math.js", qjs.Bytecode(bytecode))
iferr!=nil {
log.Fatal("Bytecode execution error:", err)
}
fmt.Printf("Fibonacci(10): %d\n", result.GetPropertyStr("fib10").Int32())
fmt.Printf("Factorial(5): %d\n", result.GetPropertyStr("fact5").Int32())
result.Free()
ProxyValue is a feature that allows you to pass Go values directly to JavaScript without full serialization, enabling efficient sharing of complex objects, functions, and resources.
ProxyValue creates a lightweight JavaScript wrapper around Go values, storing only a reference ID rather than copying the entire value. This is particularly useful for pass-through scenarios where JavaScript receives a Go value and passes it back to Go without needing to access its contents.
Key benefits:
Zero-copy data sharing - no serialization/deserialization overhead.
Pass-through efficiency - JavaScript can hold and return Go values without conversion.
Type preservation - original Go types are maintained across boundaries.
Resource efficiency - perfect for objects like context.Context, database connections, or large structs.
// Create a Go function that accepts context and a numbergoFuncWithContext:=func(c context.Context, numint) int {
// Access context values in Golog.Println("Context value:", c.Value("key"))
returnnum*2
}
// Convert Go function to JavaScript functionjsFuncWithContext, err:=qjs.ToJSValue(ctx, goFuncWithContext)
iferr!=nil {
log.Fatal("Func conversion error:", err)
}
deferjsFuncWithContext.Free()
ctx.Global().SetPropertyStr("funcWithContext", jsFuncWithContext)
// Create a helper function that returns a ProxyValuectx.SetFunc("$context", func(this*qjs.This) (*qjs.Value, error) {
// Create context as ProxyValue - JavaScript will never access its contentspassContext:=context.WithValue(context.Background(), "key", "value123")
val:=ctx.NewProxyValue(passContext)
returnval, nil
})
// JavaScript gets context as ProxyValue and passes it to Go functionresult, err:=ctx.Eval("test.js", qjs.Code(` funcWithContext($context(), 10);`))
iferr!=nil {
log.Fatal("Eval error:", err)
}
deferresult.Free()
// Output: 20log.Println("Result:", result.Int32())
// Runtime Managementrt, err:=qjs.New(options...) // Create runtimert.Close() // Cleanup runtimectx.Eval(filename, code, flags...) // Execute JavaScriptrt.Load(filename, code) // Load modulert.Compile(filename, code) // Compile to bytecode...// Context Operations ctx:=ctx// Get contextctx.Global() // Access global objectctx.SetFunc(name, fn) // Bind Go functionctx.SetAsyncFunc(name, fn) // Bind async functionctx.NewString(s) // Create JS stringctx.NewObject() // Create JS objectctx.NewProxyValue(v) // Create ProxyValue from Go value...// Value Operationsvalue.String() // Convert to Go stringvalue.Int32() // Convert to Go int32value.Bool() // Convert to Go boolvalue.GetPropertyStr(name) // Get object propertyvalue.SetPropertyStr(name, val) // Set object propertyvalue.IsQJSProxyValue() // Check if value is a ProxyValuevalue.Free() // Release memory...// ProxyValue Operations
qjs.JsValueToGo[T](value) // Extract Go value from ProxyValueqjs.ToJSValue(ctx, goValue) // Convert Go value to JS (auto-detects ProxyValue need)...