WebAssembly för nybörjare del 4: WebAssembly och JavaScript-kompanjonskap

By rik

I denna fjärde del av vår introduktion till WebAssembly ska vi fördjupa oss i samarbetet mellan WebAssembly och JavaScript.

Här får du lära dig hur du integrerar WebAssembly i din JavaScript-kod. Vi kommer också att undersöka WebAssembly JavaScript API.

WebAssembly, eller WASM, är en öppen binär standard som möjliggör för utvecklare att köra applikationer med nära-inbyggd prestanda direkt i webbläsare. Om du är ny på ämnet, rekommenderar vi att du tar en titt på de tidigare delarna av vår guide.

Låt oss börja utforska.

WebAssembly och JavaScript i samklang

I den första delen av vår WebAssembly-serie gick vi igenom hur WASM fungerar. För att skapa högpresterande webbapplikationer, måste du använda WASM API:er och funktioner från JavaScript. Vi diskuterade också hur JavaScript-ramverk utnyttjar WASM för att bygga kraftfulla applikationer.

Det är dock inte möjligt att ladda WASM-moduler som ES6-moduler med <script type=”module”> i nuläget. Det är här JavaScript kommer in i bilden. Det används för att ladda och kompilera WASM i webbläsaren. Stegen är följande:

  • Ladda .wasm-binärkod i en ArrayBuffer eller en typad array.
  • Kompilera byten med hjälp av WebAssembly.Module.
  • Skapa en instans av WebAssembly.Module med import för att få exporter som kan anropas.

Du behöver alltså börja med en förkompilerad WASM-modul. Här har du flera alternativ. Du kan skriva din kod i Rust, C/C++, AssemblyScript eller till och med TinyGo (Go) och sedan konvertera den till en .wasm-modul.

Tekniskt sett är WebAssembly ett kompileringsmål för språk. Det innebär att du skriver kod i det språk du föredrar, och sedan använder den genererade binärkoden i din applikation (webb eller icke-webb). Om du planerar att använda det på servrar måste du dessutom använda WASI för att interagera med systemet.

Eftersom WebAssembly använder linjärt minne via en expanderbar array, kan både JavaScript och WASM synkront komma åt det, vilket ger dig möjligheten att skapa funktionsrika och snabba applikationer.

Exempel på WebAssembly och JavaScript

Låt oss titta på några exempel för att demonstrera hur du kan använda WASM med JavaScript.

Som tidigare nämnts behöver du en förkompilerad WASM-modul. I det här exemplet använder vi Emscripten (C/C++). Eftersom WASM tillhandahåller ett högpresterande binärt format, kan vi köra den genererade koden tillsammans med JavaScript eller andra språk.

Verktygskonfiguration

När vi använder Emscripten behöver vi emsdk-verktyget. Det låter dig kompilera din C/C++-kod till .wasm-kod.

Kör följande kommando i din terminal. Om du inte har GIT installerat kan du följa vår guide Open Source 101: Version Control System and Git guide för att göra det.

git clone https://github.com/emscripten-core/emsdk.git
cd emsdk
#Output

[email protected]:~/Projects/WASM2$ git clone https://github.com/emscripten-core/emsdk.git
Cloning into 'emsdk'...
remote: Enumerating objects: 3566, done.
remote: Counting objects: 100% (62/62), done.
remote: Compressing objects: 100% (49/49), done.
remote: Total 3566 (delta 31), reused 38 (delta 13), pack-reused 3504
Receiving objects: 100% (3566/3566), 2.09 MiB | 2.24 MiB/s, done.
Resolving deltas: 100% (2334/2334), done.
[email protected]:~/Projects/WASM2$ cd emsdk
[email protected]:~/Projects/WASM2/emsdk$

I emdsk-mappen kör vi ett annat kommando för att hämta den senaste färdiga versionen av Emscripten.

För att göra det kör du följande kommandon.

./emsdk install latest
./emsdk activate latest
source ./emsdk_env.sh
#Output

[email protected]:~/Projects/WASM2/emsdk$ ./emsdk install latest
Resolving SDK alias 'latest' to '3.1.31'
Resolving SDK version '3.1.31' to 'sdk-releases-1eec24930cb2f56f6d9cd10ffcb031e27ea4157a-64bit'
Installing SDK 'sdk-releases-1eec24930cb2f56f6d9cd10ffcb031e27ea4157a-64bit'..
Installing tool 'node-14.18.2-64bit'..
Downloading: /home/nitt/Projects/WASM2/emsdk/zips/node-v14.18.2-linux-x64.tar.xz from https://storage.googleapis.com/webassembly/emscripten-releases-builds/deps/node-v14.18.2-linux-x64.tar.xz, 21848416 Bytes
Unpacking '/home/nitt/Projects/WASM2/emsdk/zips/node-v14.18.2-linux-x64.tar.xz' to '/home/nitt/Projects/WASM2/emsdk/node/14.18.2_64bit'
Done installing tool 'node-14.18.2-64bit'.
Installing tool 'releases-1eec24930cb2f56f6d9cd10ffcb031e27ea4157a-64bit'..
Downloading: /home/nitt/Projects/WASM2/emsdk/zips/1eec24930cb2f56f6d9cd10ffcb031e27ea4157a-wasm-binaries.tbz2 from https://storage.googleapis.com/webassembly/emscripten-releases-builds/linux/1eec24930cb2f56f6d9cd10ffcb031e27ea4157a/wasm-binaries.tbz2, 349224945 Bytes
Unpacking '/home/nitt/Projects/WASM2/emsdk/zips/1eec24930cb2f56f6d9cd10ffcb031e27ea4157a-wasm-binaries.tbz2' to '/home/nitt/Projects/WASM2/emsdk/upstream'
Done installing tool 'releases-1eec24930cb2f56f6d9cd10ffcb031e27ea4157a-64bit'.
Done installing SDK 'sdk-releases-1eec24930cb2f56f6d9cd10ffcb031e27ea4157a-64bit'.
[email protected]:~/Projects/WASM2/emsdk$ ./emsdk activate latest
Resolving SDK alias 'latest' to '3.1.31'
Resolving SDK version '3.1.31' to 'sdk-releases-1eec24930cb2f56f6d9cd10ffcb031e27ea4157a-64bit'
Setting the following tools as active:
   node-14.18.2-64bit
   releases-1eec24930cb2f56f6d9cd10ffcb031e27ea4157a-64bit

Next steps:
- To conveniently access emsdk tools from the command line,
  consider adding the following directories to your PATH:
    /home/nitt/Projects/WASM2/emsdk
    /home/nitt/Projects/WASM2/emsdk/node/14.18.2_64bit/bin
    /home/nitt/Projects/WASM2/emsdk/upstream/emscripten
- This can be done for the current shell by running:
    source "/home/nitt/Projects/WASM2/emsdk/emsdk_env.sh"
- Configure emsdk in your shell startup scripts by running:
    echo 'source "/home/nitt/Projects/WASM2/emsdk/emsdk_env.sh"' >> $HOME/.bash_profile

Det sista kommandot, ”source ./emsdk_env.sh”, ser till att emcc, Emscriptens kompilatorverktygssökväg, är korrekt inställd så att du kan använda det för att kompilera kod.

#Output

[email protected]:~/Projects/WASM2/emsdk$ source ./emsdk_env.sh
Setting up EMSDK environment (suppress these messages with EMSDK_QUIET=1)
Adding directories to PATH:
PATH += /home/nitt/Projects/WASM2/emsdk
PATH += /home/nitt/Projects/WASM2/emsdk/upstream/emscripten
PATH += /home/nitt/Projects/WASM2/emsdk/node/14.18.2_64bit/bin

Setting environment variables:
PATH = /home/nitt/Projects/WASM2/emsdk:/home/nitt/Projects/WASM2/emsdk/upstream/emscripten:/home/nitt/Projects/WASM2/emsdk/node/14.18.2_64bit/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin
EMSDK = /home/nitt/Projects/WASM2/emsdk
EMSDK_NODE = /home/nitt/Projects/WASM2/emsdk/node/14.18.2_64bit/bin/node
[email protected]:~/Projects/WASM2/emsdk$ 

Nu måste vi generera WASM-koden genom att köra följande kommando.

emcc hello-adminvista.com.c -o hello-adminvista.com.js
#Output

[email protected]:~/Projects/WASM2$ emcc hello-adminvista.com.c -o hello-adminvista.com.js
shared:INFO: (Emscripten: Running sanity checks)
cache:INFO: generating system asset: symbol_lists/1c683af19e290d0b5ca7a8747d74a76f63dcb362.txt... (this will be cached in "/home/nitt/Projects/WASM2/emsdk/upstream/emscripten/cache/symbol_lists/1c683af19e290d0b5ca7a8747d74a76f63dcb362.txt" for subsequent builds)
cache:INFO:  - ok
[email protected]:~/Projects/WASM2$ dir
emsdk  hello-adminvista.com.c  hello-adminvista.com.js  hello-adminvista.com.wasm
[email protected]:~/Projects/WASM2$ 

Som du ser får du utdata i form av ”hello-adminvista.com.js” och hello-adminvista.com.wasm. Du kan kontrollera filerna genom att köra dir i projektkatalogen.

Båda dessa filer är viktiga. Hello-adminvista.com.wasm innehåller den kompilerade koden, medan hello-adminvista.com.js-filen innehåller den JavaScript-kod som behövs för att köra den. Eftersom Emscripten stöder webb- och Node.js-körning, kan vi testa den med Node.

node hello-adminvista.com.js
#Output

[email protected]:~/Projects/WASM2$ node hello-adminvista.com.js
Hello, adminvista.com! 
[email protected]:~/Projects/WASM2$ 

Om du vill se den köras på webben, kan du generera HTML-filen med Emscripten. För att göra det, kör följande kommando.

emcc hello-adminvista.com.c -o hello-adminvista.com.html
#Output

[email protected]:~/Projects/WASM2$ emcc hello-adminvista.com.c -o hello-adminvista.com.html
[email protected]:~/Projects/WASM2$ 

För att köra HTML-filen kan du använda Python 3 HTTPServer genom att köra följande kommando.

python3 -m http.server 8000

Gå nu till http://localhost:8000/hello-adminvista.com.html för att se resultatet.

Obs: De flesta system levereras med Python förinstallerat. Om inte, kan du enkelt installera det innan du försöker köra Python3-servern.

Använda JavaScript API för att interagera med WASM

I det här avsnittet kommer vi att undersöka JavaScript WASM API närmare. Med det kommer vi att lära oss hur man laddar WASM-kod och kör den. Men låt oss först titta på koden nedan.

fetch('hello-adminvista.com.wasm').then( response =>
   response.arrayBuffer())
   .then (bytes =>
       WebAssembly.instantiate(bytes))
       .then(result=>
           alert(result.instance.exports.answer()))

Ovanstående kod använder följande JavaScript API:er.

  • fetch() webbläsar-API
  • WebAssembly.instantiate

Förutom dessa finns det andra API:er som är värda att nämna. Dessa inkluderar:

  • WebAssembly.compile
  • WebAssembly.instance
  • WebAssembly.instantiate
  • WebAssembly.instantiateStreaming

fetch() webbläsar-API

Fetch() API:et laddar nätverksresursen .wasm. Om du försöker ladda den lokalt måste du inaktivera resursdelning mellan olika ursprung för att ladda nätverksresursen. Annars kan du använda en Node-server för att göra det åt dig. För att installera och köra en Node-server, kör följande kommando.

sudo apt install npm

Kör sedan följande kommando för att starta servern.

npx http-server -o
#Output

http-server version: 14.1.1

http-server settings: 
CORS: disabled
Cache: 3600 seconds
Connection Timeout: 120 seconds
Directory Listings: visible
AutoIndex: visible
Serve GZIP Files: false
Serve Brotli Files: false
Default File Extension: none

Available on:
  http://127.0.0.1:8080
  http://192.168.0.107:8080
Hit CTRL-C to stop the server
Open: http://127.0.0.1:8080

[2023-01-28T19:22:21.137Z]  "GET /" "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/109.0.0.0 Safari/537.36 Edg/109.0.1518.70"
(node:37919) [DEP0066] DeprecationWarning: OutgoingMessage.prototype._headers is deprecated
(Use `node --trace-deprecation ...` to show where the warning was created)
[2023-01-28T19:22:21.369Z]  "GET /favicon.ico" "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/109.0.0.0 Safari/537.36 Edg/109.0.1518.70"
[2023-01-28T19:22:21.369Z]  "GET /favicon.ico" Error (404): "Not found"

Det öppnar webbläsaren där du kan se alla dina projektfiler.

Öppna nu hello-adminvista.com.html och använd utvecklarverktygen i webbläsaren. Öppna konsolen och skriv följande:

fetch(“hello-adminvista.com.wasm”);

Det returnerar följande löfte.

#Output

Promise {<pending>}
[[Prototype]]: Promise
[[PromiseState]]: "fulfilled"
[[PromiseResult]]: Response
body: (...)
bodyUsed: false
headers: Headers {}
ok: true
redirected: false
status: 200
statusText: "OK"
type: "basic"
url: "http://127.0.0.1:8080/hello-adminvista.com.wasm"
[[Prototype]]: Response

Du kan också skriva följande skript och köra det i HTML.

För att köra dina WASM-moduler på servern behöver du använda följande kod i Node.js.

const fs = require('fs');
const run = async() => {
   const buffer = fs.readFileSync("./hello-adminvista.com.wasm");
   const result = await WebAssembly.instantiate(buffer);
   console.log(result.instance.exports.answer());
};

run();

Vi rekommenderar att du läser WebAssembly JavaScript API-dokumentationen för att lära dig mer om det.

JavaScript kontra WASM

För att förstå samspelet mellan WASM och JavaScript måste vi även jämföra dem. I grund och botten är WASM snabbare och har ett binärt format för målkompilering, medan JavaScript är ett högnivåspråk. WASM:s binärkod gör det svårare att lära sig, men det finns sätt att arbeta effektivt med WASM.

De viktigaste skillnaderna mellan WASM och JavaScript är:

  • WASM är ett kompilerat språk, medan JS är ett tolkat språk. Webbläsaren måste ladda ner och parsa JavaScript vid körning, medan WASM-koden är redo att köras tack vare den förkompilerade koden.
  • WebAssembly är ett lågnivåspråk, medan JavaScript är ett högnivåspråk. JS är lätt att arbeta med tack vare sin höga nivå. Men WASM på låg nivå kan köras betydligt snabbare än JavaScript.
  • Slutligen har JavaScript fördelen av att ha en stor community. Så om du vill ha en bättre utvecklingsupplevelse är JS ett självklart val. WebAssembly är å andra sidan relativt nytt och saknar därför resurser.

Som utvecklare behöver du inte oroa dig för vilket du ska välja, eftersom JS och WASM jobbar tillsammans, inte emot varandra.

Så om du utvecklar en högpresterande applikation, kan du använda WebAssembly för de delar av koden som kräver extra prestanda. JavaScript API:et hjälper dig att hämta och använda WASM-moduler direkt i din JavaScript-kod.

Slutgiltiga tankar

Slutligen är WebAssembly en utmärkt partner till JavaScript. Det ger utvecklare möjligheten att bygga högpresterande applikationer för både webb och icke-webb, utan att försöka ersätta JavaScript.

Men kommer WASM att utvecklas till ett komplett paket och ersätta JavaScript? Med tanke på WebAssemblys mål är det möjligt, men inte särskilt troligt. Men idén om att WebAssembly i framtiden kommer att ersätta JavaScript är inte helt omöjlig.

Kolla sedan in de bästa JavaScript (JS) UI-biblioteken för att bygga moderna applikationer.