Artikel 
asliPada bulan Februari 2017, seorang anggota tim Brad Fitzpatrick go 
mengusulkan pembuatan dukungan WebAssembly dalam bahasa tersebut. Empat bulan kemudian, pada November 2017, penulis GopherJS Richard Muziol mulai menerapkan gagasan itu. Dan akhirnya, implementasi penuh ditemukan di master. Pengembang akan menerima wasme sekitar Agustus 2018, dengan versi 
go 1.11 . Akibatnya, pustaka standar mengatasi hampir semua kesulitan teknis dengan mengimpor dan mengekspor fungsi yang Anda kenal jika Anda sudah mencoba mengkompilasi C dalam wasm. Kedengarannya menjanjikan. Mari kita lihat apa yang bisa dilakukan dengan versi pertama.
Semua contoh dalam artikel ini dapat diluncurkan dari wadah buruh pelabuhan yang ada 
di repositori penulis :
docker container run -dP nlepage/golang_wasm:examples  
Lalu pergi ke 
localhost : 32XXX /, dan pergi dari satu tautan ke tautan lainnya.
Hai Wasm!
Penciptaan dasar "hello world" dan konsepnya sudah cukup 
terdokumentasi dengan baik (bahkan 
dalam bahasa Rusia ), jadi mari kita beralih ke hal-hal yang lebih halus.
Yang paling penting adalah versi Go yang baru dikompilasi yang mendukung wasm. Saya tidak akan 
menjelaskan langkah demi langkah instalasi , hanya tahu bahwa apa yang dibutuhkan sudah di master.
Jika Anda tidak ingin khawatir tentang hal ini, 
Dockerfile c go tersedia di 
repositori golub-wasm di github , atau Anda dapat mengambil gambar dari 
nlepage / golang_wasm lebih cepat.
Sekarang Anda dapat menulis 
helloworld.go tradisional dan kompilasi dengan perintah berikut:
 GOOS=js GOARCH=wasm go build -o test.wasm helioworld.go 
Variabel lingkungan GOOS dan GOARCH telah disetel di gambar 
nlepage / golang_wasm , sehingga Anda dapat menggunakan file 
Dockerfile seperti ini untuk dikompilasi:
 FROM nlepage/golang_wasm COPY helloworld.go /go/src/hello/ RUN go build -o test.wasm hello 
Langkah terakhir adalah menggunakan file 
wasm_exec.html dan 
wasm_exec.js tersedia di repositori go di 
misc/wasm atau di docker image 
nlepage / golang_wasm di 
/usr/local/go/misc/wasm/ untuk menjalankan 
test.wasm di browser (wasm_exec.js mengharapkan file binary 
test.wasm , jadi kami menggunakan nama ini).
Anda hanya perlu memberikan 3 file statis menggunakan nginx, misalnya, lalu wasm_exec.html akan menampilkan tombol “run” (ini akan menyala hanya jika 
test.wasm dimuat dengan benar).
Patut dicatat bahwa 
test.wasm harus disajikan dengan 
application/wasm jenis MIME, jika tidak browser akan menolak untuk mengeksekusinya. (mis. nginx membutuhkan 
file mime.types yang diperbarui ).
Anda dapat menggunakan gambar nginx dari 
nlepage / golang_wasm , yang sudah menyertakan tipe MIME tetap, 
wasm_exec.html dan 
wasm_exec.js dalam kode> / usr / share / nginx / html / direktori.
Sekarang klik tombol "run", lalu buka konsol browser Anda dan Anda akan melihat ucapan console.log ("Hello Wasm!").
Contoh lengkap tersedia di 
sini .
Panggil JS dari Go
Sekarang kita telah berhasil meluncurkan biner WebAssembly pertama yang dikompilasi dari Go, mari kita lihat lebih dekat fitur yang disediakan.
Paket syscall / js baru telah ditambahkan ke perpustakaan standar. Pertimbangkan file utama, 
js.goTipe 
js.Value baru 
js.Value yang mewakili nilai JavaScript.
Ini menawarkan API sederhana untuk mengelola variabel JavaScript:
- js.Value.Get()dan- js.Value.Set()mengembalikan dan mengatur nilai bidang objek.
- js.Value.Index()dan- js.Value.SetIndex()mengakses objek dengan membaca dan menulis indeks.
- js.Value.Call()memanggil metode objek sebagai fungsi.
- js.Value.Invoke()menyebut objek itu sendiri sebagai fungsi.
- js.Value.New()memanggil operator baru dan menggunakan pengetahuannya sendiri sebagai konstruktor.
- Beberapa metode lagi untuk mendapatkan nilai JavaScript dalam tipe Go yang sesuai, misalnya js.Value.Int()ataujs.Value.Bool().
Dan metode menarik tambahan:
- js.Undefined()akan memberikan js.Value- undefinedsesuai.
- js.Null()akan memberikan- js.Value- nullsesuai.
- js.Global()akan mengembalikan- js.Valuememberikan akses ke lingkup global.
- js.ValueOf()menerima tipe Go primitif dan mengembalikan- js.Valuebenar
Alih-alih menampilkan pesan di os.StdOut, mari kita tampilkan di jendela notifikasi menggunakan 
window.alert() .
Karena kita berada di browser, ruang lingkup global adalah sebuah jendela, jadi pertama-tama Anda perlu mendapatkan lansiran () dari ruang lingkup global:
 alert := js.Global().Get("alert") 
Sekarang kita memiliki variabel 
alert , dalam bentuk 
js.Value , yang merupakan referensi ke 
window.alert JS, dan Anda dapat menggunakan fungsi untuk memanggil melalui 
js.Value.Invoke() :
 alert.Invoke("Hello wasm!") 
Seperti yang Anda lihat, tidak perlu memanggil js.ValueOf () sebelum meneruskan argumen ke Invoke, dibutuhkan 
interface{} sewenang-wenang dan meneruskan nilai melalui ValueOf itu sendiri.
Sekarang program baru kita akan terlihat seperti ini:
 package main import ( "syscall/js" ) func main() { alert := js.Global().Get("alert") alert.Invoke("Hello Wasm!") } 
Seperti pada contoh pertama, Anda hanya perlu membuat file bernama 
test.wasm , dan biarkan 
wasm_exec.html dan 
wasm_exec.js seperti semula.
Sekarang, ketika kita mengklik tombol "Jalankan", jendela peringatan muncul dengan pesan kami.
Contoh yang berfungsi ada di folder 
examples/js-call .
Panggil Pergi dari JS.
Memanggil JS dari Go cukup sederhana, mari kita lihat lebih dekat pada paket 
syscall/js , file kedua yang akan dilihat adalah 
callback.go .
- js.Callbacktipe pembungkus untuk fungsi Go, untuk digunakan dalam JS.
- js.NewCallback()fungsi yang mengambil fungsi (menerima sepotong- js.Valuedan mengembalikan apa-apa), dan mengembalikan- js.Callback.
- Beberapa mekanisme untuk mengelola panggilan balik aktif dan js.Callback.Release(), yang harus dipanggil untuk menghancurkan panggilan balik itu.
- js.NewEventCallback()mirip dengan- js.NewCallback(), tetapi fungsi yang dibungkus hanya menerima 1 argumen - sebuah peristiwa.
Mari kita coba melakukan sesuatu yang sederhana: jalankan Go 
fmt.Println() dari sisi JS.
Kami akan membuat beberapa perubahan pada 
wasm_exec.html untuk bisa mendapatkan panggilan balik dari Go untuk memanggilnya.
 async function run() { console.clear(); await go.run(inst); inst = await WebAssembly.instantiate(mod, go.ImportObject);  
Ini meluncurkan binary wasme dan menunggu untuk menyelesaikannya, kemudian menginisialisasi ulang untuk menjalankan berikutnya.
Mari kita tambahkan fungsi baru yang akan menerima dan menyimpan panggilan balik Go dan mengubah status 
Promise setelah selesai:
 let printMessage  
Sekarang mari kita adaptasikan fungsi 
run() untuk menggunakan callback:
 async function run() { console.clear()  
Dan ini ada di pihak JS!
Sekarang, di bagian Go, Anda perlu membuat panggilan balik, mengirimkannya ke sisi JS dan menunggu fungsi yang diperlukan.
  var done = make(chan struct{}) 
Maka mereka harus menulis fungsi 
printMessage() :
 func printMessage(args []js.Value) { message := args[0].Strlng() fmt.Println(message) done <- struct{}{}  
Argumen dilewatkan melalui slice 
[]js.Value , jadi Anda perlu memanggil 
js.Value.String() pada elemen slice pertama untuk mendapatkan pesan di baris Go.
Sekarang kita dapat membungkus fungsi ini dalam panggilan balik:
 callback := js.NewCallback(printMessage) defer callback.Release()  
Kemudian panggil fungsi JS 
setPrintMessage() , sama seperti memanggil 
window.alert() :
 setPrintMessage := js.Global.Get("setPrintMessage") setPrintMessage.Invoke(callback) 
Hal terakhir yang harus dilakukan adalah menunggu panggilan balik dipanggil di utama:
 <-done 
Bagian terakhir ini penting karena callback dijalankan dalam goroutine khusus, dan goroutine utama harus menunggu panggilan balik dipanggil, jika biner wasm akan dihentikan sebelum waktunya.
Program Go yang dihasilkan akan terlihat seperti ini:
 package main import ( "fmt" "syscall/js" ) var done = make(chan struct{}) func main() { callback := js.NewCallback(prtntMessage) defer callback.Release() setPrintMessage := js.Global().Get("setPrintMessage") setPrIntMessage.Invoke(callback) <-done } func printMessage(args []js.Value) { message := args[0].Strlng() fmt.PrintIn(message) done <- struct{}{} } 
Seperti pada contoh sebelumnya, buat file bernama 
test.wasm . Kita juga perlu mengganti 
wasm_exec.html dengan versi kita, dan 
wasm_exec.js dapat menggunakan kembali 
wasm_exec.js .
Sekarang, ketika Anda menekan tombol "run", seperti dalam contoh pertama kami, pesan dicetak di konsol browser, tetapi kali ini jauh lebih baik! (Dan lebih keras.)
Contoh yang berfungsi dalam tawaran file buruh pelabuhan tersedia di folder 
examples/go-call .
Kerja panjang
Memanggil Go dari JS sedikit lebih rumit daripada memanggil JS dari Go, terutama di sisi JS.
Hal ini terutama disebabkan oleh fakta bahwa Anda perlu menunggu sampai hasil panggilan balik Go diteruskan ke sisi JS.
Mari kita coba sesuatu yang lain: mengapa tidak mengatur binary wasm, yang tidak akan berakhir tepat setelah panggilan balik, tetapi akan terus bekerja dan menerima panggilan lain.
Kali ini, mari kita mulai dari sisi Go, dan seperti pada contoh kita sebelumnya, kita perlu membuat panggilan balik dan mengirimkannya ke sisi JS.
Tambahkan penghitung panggilan untuk melacak berapa kali fungsi telah dipanggil.
Fungsi 
printMessage() baru kami 
printMessage() akan mencetak pesan yang diterima dan nilai penghitung:
 var no int func printMessage(args []js.Value) { message := args[0].String() no++ fmt.Printf("Message no %d: %s\n", no, message) } 
Membuat panggilan balik dan mengirimnya ke sisi JS sama seperti pada contoh sebelumnya:
 callback := js.NewCallback(printMessage) defer callback.Release() setPrintMessage := js.Global().Get("setPrintMessage") setPrIntMessage.Invoke(callback) 
Tapi kali ini kami belum memiliki saluran untuk memberi tahu kami tentang penghentian goroutine utama. Salah satu caranya adalah dengan mengunci goroutin utama secara permanen dengan 
select{} kosong 
select{} :
 select{} 
Ini tidak memuaskan, binary wasm kami hanya akan menggantung di memori sampai tab browser ditutup.
Anda dapat mendengarkan acara 
beforeunload pada halaman, Anda akan membutuhkan panggilan balik kedua untuk menerima acara dan memberitahukan goroutine utama melalui saluran:
 var beforeUnloadCh = make(chan struct{}) 
Kali ini, fungsi 
beforeUnload() baru hanya akan menerima acara tersebut, sebagai argumen tunggal 
js.Value :
 func beforeUnload(event js.Value) { beforeUnloadCh <- struct{}{} } 
Kemudian bungkus dengan callback menggunakan 
js.NewEventCallback() dan daftarkan di sisi JS:
 beforeUnloadCb := js.NewEventCallback(0, beforeUnload) defer beforeUnloadCb.Release() addEventLtstener := js.Global().Get("addEventListener") addEventListener.Invoke("beforeunload", beforeUnloadCb) 
Akhirnya, ganti 
select pemblokiran kosong dengan membaca dari saluran 
beforeUnloadCh :
 <-beforeUnloadCh fmt.Prtntln("Bye Wasm!") 
Program akhir terlihat seperti ini:
 package main import ( "fmt" "syscall/js" ) var ( no int beforeUnloadCh = make(chan struct{}) ) func main() { callback := js.NewCallback(printMessage) defer callback.Release() setPrintMessage := js.Global().Get("setPrintMessage") setPrIntMessage.Invoke(callback) beforeUnloadCb := js.NewEventCallback(0, beforeUnload) defer beforeUnloadCb.Release() addEventLtstener := js.Global().Get("addEventListener") addEventListener.Invoke("beforeunload", beforeUnloadCb) <-beforeUnloadCh fmt.Prtntln("Bye Wasm!") } func printMessage(args []js.Value) { message := args[0].String() no++ fmt.Prtntf("Message no %d: %s\n", no, message) } func beforeUnload(event js.Value) { beforeUnloadCh <- struct{}{} } 
Sebelumnya, di sisi JS, unduhan biner wasme tampak seperti ini:
 const go = new Go() let mod, inst WebAssembly .instantiateStreaming(fetch("test.wasm"), go.importObject) .then((result) => { mod = result.module inst = result.Instance document.getElementById("runButton").disabled = false }) 
Mari kita adaptasi untuk menjalankan biner segera setelah memuat:
 (async function() { const go = new Go() const { instance } = await WebAssembly.instantiateStreaming( fetch("test.wasm"), go.importObject ) go.run(instance) })() 
Dan ganti tombol "Jalankan" dengan bidang pesan dan tombol untuk memanggil 
printMessage() :
 <input id="messageInput" type="text" value="Hello Wasm!"> <button onClick="printMessage(document.querySelector('#messagelnput').value);" id="prtntMessageButton" disabled> Print message </button> 
Akhirnya, fungsi 
setPrintMessage() , yang menerima dan menyimpan panggilan balik, harus lebih sederhana:
 let printMessage; function setPrintMessage(callback) { printMessage = callback; document.querySelector('#printMessageButton').disabled = false; } 
Sekarang, ketika kita mengklik tombol "Cetak pesan", Anda akan melihat pesan pilihan kami dan penghitung panggilan dicetak di konsol browser.
Jika kita mencentang kotak Preserve log dari konsol browser dan menyegarkan halaman, kita akan melihat pesan "Bye Wasm!".
Sumber tersedia di folder 
examples/long-running di github.
Lalu?
Seperti yang Anda lihat, API 
syscall/js dipelajari melakukan tugasnya dan memungkinkan Anda menulis hal-hal kompleks dengan sedikit kode. Anda dapat menulis kepada 
penulis jika Anda tahu metode yang lebih sederhana.
Saat ini tidak mungkin untuk mengembalikan nilai ke JS langsung dari panggilan balik Go.
Ingatlah bahwa semua panggilan balik dilakukan di goroutin yang sama, jadi jika Anda melakukan beberapa operasi pemblokiran dalam panggilan balik tersebut, jangan lupa untuk membuat goroutin baru, jika tidak, Anda akan memblokir eksekusi semua panggilan balik lainnya.
Semua fitur bahasa dasar sudah tersedia, termasuk konkurensi. Untuk saat ini, semua goroutin akan bekerja dalam satu utas, tetapi ini 
akan berubah di masa mendatang .
Dalam contoh kami, kami hanya menggunakan paket fmt dari perpustakaan standar, tetapi semuanya tersedia yang tidak mencoba untuk melarikan diri dari kotak pasir.
Sistem file tampaknya didukung melalui Node.js.
Akhirnya, bagaimana dengan kinerja? Akan menarik untuk menjalankan beberapa tes untuk melihat bagaimana Go wasm membandingkan dengan kode JS murni yang setara. Seseorang 
hajimehoshi membuat pengukuran tentang bagaimana lingkungan yang berbeda bekerja dengan bilangan bulat, tetapi tekniknya tidak terlalu jelas.
Jangan lupa bahwa Go 1.11 belum dirilis secara resmi. Menurut saya itu sangat bagus untuk teknologi eksperimental. Mereka yang tertarik dengan tes kinerja 
dapat menyiksa browser mereka .
Ceruk utama, seperti yang dicatat penulis, adalah transfer dari server ke klien dari kode go yang ada. Tetapi dengan standar baru, Anda dapat membuat 
aplikasi sepenuhnya offline , dan kode wasm disimpan dalam bentuk yang dikompilasi. Anda dapat mentransfer banyak utilitas ke web, setuju, dengan nyaman?