Ngày 04 tháng 10 năm 2019 - Công nghệ máy tính
WebAssembly (viết tắt là wasm) là một định dạng nhị phân mới có khả năng chạy trên trình duyệt web hiện đại. Nó sử dụng ngôn ngữ giống mã máy ở cấp độ thấp để biên dịch các ngôn ngữ cấp cao như C++/Rust/Go thành dạng nhị phân m88vin - cổng game quốc tế và thực thi trên trình duyệt web. Hiệu suất của nó gần đạt đến mức bản địa, đồng thời có thể gọi qua lại với JavaScript, từ đó cung cấp một cách tiếp cận mới (kết hợp hiệu suất Rio88 Game Bài Twin của WebAssembly với khả năng biểu diễn của JS) để phát triển ứng dụng.
Từ phiên bản Go 1.11, Go đã bắt đầu hỗ trợ thử nghiệm WebAssembly. Mặc dù hiện tại nó vẫn đang ở giai đoạn sơ khai với nhiều vấn đề như kích thước tệp nhị phân lớn sau khi biên dịch, khó gỡ lỗi, nhưng điều này không ngăn cản chúng ta trải nghiệm công nghệ này. Chúng ta hy vọng rằng trong các phiên bản sau, nhóm phát triển chính thức sẽ dần hoàn thiện và tối ưu hóa hơn nữa.
1. Chào mừng WebAssembly
Dưới đây là chương trình Go đơn giản nhất test.go
. Làm thế nào để chạy chương trình này dưới dạng WebAssembly trên trình duyệt?
package main
import "fmt"
func main() {
fmt.Println("Chào Assembly!")
}
Biên dịch thành tệp .wasm Cần chỉ định GOOS là js và GOARCH là wasm trước khi biên dịch.
$ GOOS=js GOARCH=wasm go build -o test.wasm test.go
Bạn sẽ thấy thêm một tệp test.wasm trong thư mục hiện tại.
$ ls -lht
... 2.2M ngày 4 tháng 10 lúc 15:36 test.wasm
... 76B ngày 4 tháng 10 lúc 15:31 test.go
Viết file index.html Sau đây là file HTML giúp tải và chạy tệp test.wasm theo chuẩn WebAssembly.
<html>
<head>
<meta charset="utf-8"/>
<script src="wasm_exec.js"></script>
<script>
const go = new Go();
WebAssembly.instantiateStreaming(fetch("test.wasm"), go.importObject).then((result) => {
go.run(result.instance);
});
</script>
</head>
<body></body>
</html>
Lưu ý rằng đoạn mã trên sử dụng một tập tin phụ thuộc tên là wasm_exec.js
, nằm trong $GOROOT/misc/wasm
. Bạn cần sao chép nó vào thư mục hiện tại.
$ cp $GOROOT/misc/wasm/wasm_exec.js .
Chạy .wasm trên trình duyệt Hiện tại, bạn có bốn tệp trong thư mục hiện tại:
$ ls -lht
... 14K ngày 4 tháng 10 lúc 15:45 wasm_exec.js
... 354B ngày 4 tháng 10 lúc 15:43 index.html
... 2.2M ngày 4 tháng 10 lúc 15:36 test.wasm
... 76B ngày 4 tháng 10 lúc 15:31 test.go
Sử dụng công cụ dòng lệnh goexec
để khởi động server web và phục vụ tài nguyên từ thư mục hiện tại.
$ goexec 'http.ListenAndServe(":8080", http.FileServer(http.Dir(".")))'
Nhập địa chỉ localhost:8080/index.html vào trình duyệt, bạn sẽ thấy thông báo “Chào Assembly!” được hiển thị trong console.
Chạy bằng node.js
Tập tin go_js_wasm_exec
nằm trong $GOROOT/misc/wasm/
cho phép chạy .wasm
dưới dạng node.js.
$ GOOS=js GOARCH=wasm go run -exec="$GOROOT/misc/wasm/go_js_wasm_exec" test.go
Chào Assembly!
Thêm đường dẫn tới $GOROOT/misc/wasm/go_js_wasm_exec
vào biến môi trường PATH để chạy mà không cần tham số -exec
.
$ export PATH="$PATH:$GOROOT/misc/wasm"
$ GOOS=js GOARCH=wasm go run test.go
Chào Assembly!
2. Xây dựng một máy tính đơn giản
Hãy tạo một máy tính cộng đơn giản, liên quan đến việc xử lý DOM. File index.html
sẽ chứa ba thẻ input (hai đầu vào số và một đầu ra kết quả), cùng với một nút button để tính toán.
index.html
<html>
<head>
<meta charset="utf-8"/>
<script src="wasm_exec.js"></script>
<script>
const go = new Go();
WebAssembly.instantiateStreaming(fetch("test.wasm"), go.importObject).then((result) => {
go.run(result.instance);
});
</script>
</head>
<body>
<input id="num1" type="number" />
+
<input id="num2" type="number" />
=
<input id="rlt" type="number" readonly="readonly" />
<button id="compute">Tính toán</button>
</body>
</html>
test.go
package main
import (
"fmt"
"strconv"
"syscall/js"
)
func registerCallbackFunc() {
cb := js.FuncOf(func(this js.Value, args []js.Value) interface{} {
fmt.Println("Nút đã được nhấn")
num1 := getElementById("num1").Get("value").String()
v1, err := strconv.Atoi(num1)
if nil != err {
panic(err)
}
num2 := getElementById("num2").Get("value").String()
v2, err := strconv.Atoi(num2)
if nil != err {
panic(err)
}
rlt := v1 + v2
getElementById("rlt").Set("value", rlt)
return nil
})
getElementById("compute").Call("addEventListener", "click", cb)
}
func getElementById(id string) js.Value {
return js.Global().Get("document").Call("getElementById", id)
}
func main() {
done := make(chan struct{}, 0)
registerCallbackFunc()
<-done
}
Biên dịch và chạy trên trình duyệt
$ GOOS=js GOARCH=wasm go build -o test.wasm test.go
$ goexec 'http.ListenAndServe(":8080", http.FileServer(http.Dir(".")))'
Kết quả chạy sẽ trông như hình bên dưới.
3. Vấn đề và Triển vọng
Do Go là ngôn ngữ có bộ thu gom rác (garbage collector), nên tệp nhị phân wasm duy nhất sau khi biên dịch cần phải mang theo cả bộ thu gom rác và runtime, dẫn đến kích thước rất lớn (ít nhất cũng >2MB). Điều này khá nghiêm trọng đối với một ứng dụng web. Cộng đồng đã phát triển một số công cụ tối ưu hóa và nén dữ liệu để giảm thiểu kích thước tập tin. Ngoài ra, thay vì sử dụng SDK tiêu chuẩn của Go, người ta có thể sử dụng môi trường cơ bản nhẹ hơn như TinyGo.
Một vấn đề khác là API liên quan đến WebAssembly trong Go còn hạn chế. Chúng ta kỳ vọng rằng trong các phiên bản tương lai, sẽ có thêm các gói WebAssembly dễ sử dụng và phong phú hơn.
[1] Link tài liệu [2] Link ví dụ [3] Link tham khảo
#Golang