By using this site, you agree to the Privacy Policy and Terms of Use.
Accept
World of SoftwareWorld of SoftwareWorld of Software
  • News
  • Software
  • Mobile
  • Computing
  • Gaming
  • Videos
  • More
    • Gadget
    • Web Stories
    • Trending
    • Press Release
Search
  • Privacy
  • Terms
  • Advertise
  • Contact
Copyright © All Rights Reserved. World of Software.
Reading: Running Modern ES2023 JavaScript Inside Go Using QJS and WebAssembly
Share
Sign In
Notification Show More
Font ResizerAa
World of SoftwareWorld of Software
Font ResizerAa
  • Software
  • Mobile
  • Computing
  • Gadget
  • Gaming
  • Videos
Search
  • News
  • Software
  • Mobile
  • Computing
  • Gaming
  • Videos
  • More
    • Gadget
    • Web Stories
    • Trending
    • Press Release
Have an existing account? Sign In
Follow US
  • Privacy
  • Terms
  • Advertise
  • Contact
Copyright © All Rights Reserved. World of Software.
World of Software > News > Running Modern ES2023 JavaScript Inside Go Using QJS and WebAssembly
News

Running Modern ES2023 JavaScript Inside Go Using QJS and WebAssembly

News Room
Last updated: 2025/12/28 at 2:19 AM
News Room Published 28 December 2025
Share
Running Modern ES2023 JavaScript Inside Go Using QJS and WebAssembly
SHARE

QJS is a CGO-free, modern JavaScript runtime for Go that embeds the QuickJS engine in a WebAssembly module and runs it with Wazero, providing Go applications with a sandboxed ES2023 environment with async/await and tight Go–JS interoperability.

QJS targets Go developers who want to run modern JavaScript inside Go processes without linking to native C libraries. Instead of binding QuickJS directly via CGO, it compiles QuickJS-NG to WebAssembly and executes it under Wazero, providing:

  • Full ES2023 support (modules, async/await, BigInt, etc.).
  • A fully sandboxed, memory-safe execution model.
  • No CGO toolchain or C runtime dependency.

The runtime is compatible with Go 1.22+ and is distributed as a regular Go module:


go get github.com/fastschema/qjs

and then:


import "github.com/fastschema/qjs"

QJS exposes a Runtime and Context API that lets Go code evaluate JavaScript, bind functions, and exchange data structures. A minimal example creates a runtime, evaluates a script, and reads structured results back into 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 functions can be exposed to JavaScript, and JS functions can be converted back to typed Go callables. For example, binding a Go function:


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 also supports converting richer Go structs to JS values and back, including methods that can be invoked from JavaScript and then deserialised to typed Go values.

To avoid repeated serialisation of large or opaque Go objects, QJS introduces Proxy, a lightweight JavaScript wrapper that holds only a reference to a Go value. This is useful for contexts, DB handles, or large structs that JS should pass through without inspecting:


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 receives the proxy and passes it back into Go, where JsValueToGo recovers the underlying value and type.QJS supports async/await by allowing Go to resolve JS promises asynchronously. A Go async function can schedule work and resolve a promise:


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 then awaits it:


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

The runtime can be used to implement HTTP handlers in JavaScript while keeping the server in Go. For example, routes like /about and /contact Are defined in JS, precompiled to bytecode, and executed from a pool of runtimes:


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()
})

The Pool type manages multiple runtimes for concurrent workloads, and examples show worker goroutines borrowing a runtime, executing JS, and returning it to the pool.

Because QuickJS runs inside Wazero, QJS offers filesystem and network isolation by default, with explicit configuration required to expose additional capabilities. The Option struct lets users set working directory, memory limits, stack size, execution timeouts, and GC thresholds:


type Option struct {
    CWD             string
    MaxStackSize    int
    MemoryLimit     int
    MaxExecutionTime int
    GCThreshold     int
}

According to benchmarks in the repository, point QJS is very competitive with Goja and ModerncQuickJS, with lower memory usage and execution time across both factorial calculations and AreWeFastYet-style microbenchmarks. Both benchmarks were run on Linux machines with an AMD Ryzen 7 7840HS and 32GB RAM.

Computing factorial(10) 1,000,000 times












Iteration GOJA ModerncQuickJS QJS
1 1.128s 1.897s 737.635ms
2 1.134s 1.936s 742.670ms
3 1.123s 1.898s 738.737ms
4 1.120s 1.900s 754.692ms
5 1.132s 1.918s 756.924ms
Average 1.127s 1.910s 746.132ms
Total 5.637s 9.549s 3.731s
Speed 1.51x 2.56x 1.00x

AreWeFastYet V8-V7














Metric GOJA ModerncQuickJS QJS
Richards 345 189 434
DeltaBlue 411 205 451
Crypto 203 305 393
RayTrace 404 347 488
EarleyBoyer 779 531 852
RegExp 381 145 142
Splay 1289 856 1408
NavierStokes 324 436 588
Score (version 7) 442 323 498
Duration (seconds) 78.349s 97.240s 72.004s

With this design, QJS targets Go developers who need secure plugin systems, user-provided scripting, or embedded business logic written in JavaScript, without bringing a C toolchain or CGO into their build and deployment pipeline.

Sign Up For Daily Newsletter

Be keep up! Get the latest breaking news delivered straight to your inbox.
By signing up, you agree to our Terms of Use and acknowledge the data practices in our Privacy Policy. You may unsubscribe at any time.
Share This Article
Facebook Twitter Email Print
Share
What do you think?
Love0
Sad0
Happy0
Sleepy0
Angry0
Dead0
Wink0
Previous Article Meet the team that hunts government spyware Meet the team that hunts government spyware
Next Article I ran 1,000km to test the best running watches in the UK – here are my favourites I ran 1,000km to test the best running watches in the UK – here are my favourites
Leave a comment

Leave a Reply Cancel reply

Your email address will not be published. Required fields are marked *

Stay Connected

248.1k Like
69.1k Follow
134k Pin
54.3k Follow

Latest News

What are open earbuds? Here’s everything you need to know.
What are open earbuds? Here’s everything you need to know.
News
Resideo's Scott Harkins talks about the new Honeywell Home X8S on HomeKit Insider
Resideo's Scott Harkins talks about the new Honeywell Home X8S on HomeKit Insider
News
My best phone purchase of 2025 isn’t an Android after all
My best phone purchase of 2025 isn’t an Android after all
News
Mediamarkt also has its own brands in technology and cooking with devices ranging from TVs to air fryers.
Mediamarkt also has its own brands in technology and cooking with devices ranging from TVs to air fryers.
Mobile

You Might also Like

What are open earbuds? Here’s everything you need to know.
News

What are open earbuds? Here’s everything you need to know.

9 Min Read
Resideo's Scott Harkins talks about the new Honeywell Home X8S on HomeKit Insider
News

Resideo's Scott Harkins talks about the new Honeywell Home X8S on HomeKit Insider

1 Min Read
My best phone purchase of 2025 isn’t an Android after all
News

My best phone purchase of 2025 isn’t an Android after all

9 Min Read
NY Governor Hochul signs bill requiring warning labels on ‘addictive’ social media |  News
News

NY Governor Hochul signs bill requiring warning labels on ‘addictive’ social media | News

3 Min Read
//

World of Software is your one-stop website for the latest tech news and updates, follow us now to get the news that matters to you.

Quick Link

  • Privacy Policy
  • Terms of use
  • Advertise
  • Contact

Topics

  • Computing
  • Software
  • Press Release
  • Trending

Sign Up for Our Newsletter

Subscribe to our newsletter to get our newest articles instantly!

World of SoftwareWorld of Software
Follow US
Copyright © All Rights Reserved. World of Software.
Welcome Back!

Sign in to your account

Lost your password?