W czwartej odsłonie naszego cyklu dla adeptów WebAssembly, zgłębimy tajniki tej technologii i jej współdziałania z JavaScriptem.
W tym artykule dowiesz się, jak integrować WebAssembly z Twoim kodem JavaScript. Ponadto, przybliżymy Ci działanie interfejsu API WebAssembly w JavaScript.
WebAssembly to standard otwarty, reprezentowany przez binarny format, umożliwiający deweloperom uruchamianie aplikacji z wydajnością zbliżoną do natywnej w środowisku przeglądarki internetowej. Jeśli nie miałeś jeszcze okazji, zachęcamy do zapoznania się z wcześniejszymi częściami naszego przewodnika.
Przejdźmy zatem do konkretów.
Wykorzystanie WebAssembly w połączeniu z JavaScript
W pierwszej części naszego tutorialu poświęconego WebAssembly, wyjaśniliśmy mechanizm działania WASM. Aby tworzyć aplikacje internetowe o wysokiej sprawności, niezbędne jest stosowanie API i funkcji WASM z poziomu JavaScript. Omówiliśmy również, jak frameworki JavaScript mogą wykorzystywać WASM do budowy wysoce efektywnych aplikacji.
Warto zaznaczyć, że aktualnie nie ma możliwości bezpośredniego ładowania modułów WASM, tak jak ma to miejsce w przypadku modułów ES6, za pomocą znacznika <script type=” module”>. Tutaj właśnie wkracza JavaScript, odgrywając kluczową rolę w procesie ładowania i kompilacji WASM w przeglądarce. Procedura ta obejmuje następujące etapy:
- Załadowanie bajtów .wasm do obiektu ArrayBuffer lub tablicy typowanej.
- Użycie WebAssembly.Module do skompilowania załadowanych bajtów.
- Utworzenie instancji WebAssembly.Module z importami, aby uzyskać dostęp do wywoływalnych eksportów.
Zatem, punktem wyjścia jest posiadanie wstępnie skompilowanego modułu WASM. Masz tu szeroki wybór, możesz programować w Rust, C/C++, AssemblyScript, a nawet TinyGo (Go), a następnie przekonwertować swój kod do formatu .wasm.
Z technicznego punktu widzenia WebAssembly jest celem kompilacji dla różnorodnych języków programowania. Oznacza to, że najpierw tworzysz kod w wybranym języku, a następnie używasz wygenerowanego kodu binarnego w swojej aplikacji (internetowej lub innej). Dodatkowo, jeśli zamierzasz używać go na serwerach, będziesz potrzebował WASI, aby komunikować się z systemem.
WebAssembly operuje na pamięci liniowej poprzez rozszerzalną tablicę, co umożliwia zarówno JavaScript, jak i WASM synchroniczny dostęp do niej. Ta właściwość stwarza dogodne warunki do tworzenia złożonych i wydajnych aplikacji.
Przykłady praktycznego wykorzystania WebAssembly i JavaScript
Posłużmy się przykładami, aby lepiej zrozumieć, jak możesz łączyć WASM z JavaScriptem.
Jak już wspomniano, niezbędny jest wstępnie skompilowany moduł WASM. W tym pokazie użyjemy Emscripten (C/C++). Dzięki wysokowydajnemu binarnemu formatowi WASM, możemy uruchamiać wygenerowany kod równolegle z JavaScriptem lub innymi językami.
Konfiguracja środowiska
Ponieważ korzystamy z Emscripten, musimy najpierw zainstalować narzędzie emsdk. Pozwoli nam ono na transformację kodu C/C++ do postaci .wasm.
Wystarczy uruchomić poniższą komendę w terminalu. Jeśli nie posiadasz zainstalowanego GIT, skorzystaj z naszego przewodnika Open Source 101: System kontroli wersji i Git.
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$
W folderze emsdk wywołujemy komendę, by pobrać najnowszą, gotową do użycia wersję Emscripten.
W tym celu użyjemy poniższych poleceń.
./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
Ostatnia komenda, „źródło ./emsdk_env.sh”, ustawia ścieżkę do narzędzia emcc, czyli kompilatora Emscripten, umożliwiając korzystanie z niego do kompilowania kodu.
#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$
Teraz, aby wygenerować kod wasm, uruchamiamy następującą komendę.
emcc hello-newsblog.pl.c -o hello-newsblog.pl.js
#Output [email protected]:~/Projects/WASM2$ emcc hello-newsblog.pl.c -o hello-newsblog.pl.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-newsblog.pl.c hello-newsblog.pl.js hello-newsblog.pl.wasm [email protected]:~/Projects/WASM2$
W efekcie uzyskujemy pliki „hello-newsblog.pl.js” oraz hello-newsblog.pl.wasm. Możesz sprawdzić zawartość katalogu projektu, używając polecenia dir.
Obydwa pliki są niezbędne. Plik hello-newsblog.pl.wasm zawiera skompilowany kod. Natomiast plik hell-geefklare.js jest skryptem JavaScript niezbędnym do uruchomienia kodu. Emscripten umożliwia uruchamianie aplikacji zarówno w przeglądarce, jak i w Node.js. Wykorzystajmy Node.js do przeprowadzenia testu.
node hello-newsblog.pl.js
#Output [email protected]:~/Projects/WASM2$ node hello-newsblog.pl.js Hello, newsblog.pl! [email protected]:~/Projects/WASM2$
Aby przetestować działanie kodu w przeglądarce, Emscripten pozwala na wygenerowanie pliku HTML. W tym celu, uruchom następujące polecenie.
emcc hello-newsblog.pl.c -o hello-newsblog.pl.html
#Output [email protected]:~/Projects/WASM2$ emcc hello-newsblog.pl.c -o hello-newsblog.pl.html [email protected]:~/Projects/WASM2$
Aby uruchomić plik HTML, możesz skorzystać z serwera HTTP Python 3. Wystarczy uruchomić poniższą komendę:
python3 -m http.server 8000
Następnie przejdź pod adres http://localhost:8000/hello-newsblog.pl.html, aby zobaczyć wynik działania programu.
W większości systemów Python jest już domyślnie zainstalowany. Jeśli jednak nie posiadasz go w swoim systemie, zainstaluj go przed uruchomieniem serwera Python3.
Interfejs API JavaScript do pracy z WASM
W tej sekcji przyjrzymy się bliżej interfejsowi API WebAssembly w JavaScript. Z jego pomocą poznamy proces ładowania i uruchamiania kodu WASM. Na początek, przeanalizujmy poniższy kod:
fetch('hello-newsblog.pl.wasm').then( response => response.arrayBuffer()) .then (bytes => WebAssembly.instantiate(bytes)) .then(result=> alert(result.instance.exports.answer()))
Powyższy przykład wykorzystuje następujące API JavaScript:
- fetch() API przeglądarki
- WebAssembly.instantiate
Oprócz tego, istnieje kilka innych API, na które warto zwrócić uwagę, są to:
- WebAssembly.compile
- WebAssembly.instance
- WebAssembly.instantiate
- WebAssembly.instantiateStreaming
fetch() API przeglądarki
Funkcja API fetch() służy do ładowania zasobu sieciowego .wasm. W przypadku, gdy próbujesz załadować zasób lokalnie, musisz wyłączyć mechanizm współdzielenia zasobów między źródłami (CORS), aby poprawnie załadować zasób sieciowy. Alternatywnie, możesz wykorzystać serwer węzłów, aby to za Ciebie wykonał. Aby zainstalować i uruchomić serwer węzłów, użyj następującego polecenia:
sudo apt install npm
Następnie, aby uruchomić serwer, skorzystaj z tej komendy:
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"
Spowoduje to otwarcie okna przeglądarki, w którym zobaczysz wszystkie pliki swojego projektu.
Teraz otwórz plik hello-newsblog.pl.html i włącz narzędzia deweloperskie przeglądarki. Następnie w konsoli wpisz następujące polecenie:
fetch(„hello-newsblog.pl.wasm”);
Otrzymasz następującą obietnicę (Promise):
#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-newsblog.pl.wasm" [[Prototype]]: Response
Możesz również umieścić poniższy kod w skrypcie i uruchomić go za pośrednictwem pliku HTML.
Aby uruchomić moduł wasm na serwerze, w Node.js, wykorzystaj następujący kod:
const fs = require('fs'); const run = async() => { const buffer = fs.readFileSync("./hello-newsblog.pl.wasm"); const result = await WebAssembly.instantiate(buffer); console.log(result.instance.exports.answer()); }; run();
Zachęcamy do zapoznania się z dokumentacją WebAssembly JavaScript API, by poszerzyć swoją wiedzę w tym temacie.
Porównanie JavaScript i WASM
Aby lepiej zrozumieć relację między WASM a JavaScript, warto je porównać. Generalnie, WASM charakteryzuje się większą szybkością działania i formą binarną, która jest celem kompilacji, podczas gdy JavaScript jest językiem wysokiego poziomu. Kod binarny WASM sprawia, że jego nauka jest bardziej skomplikowana, jednak istnieją sposoby na efektywne wykorzystanie WASM.
Główne różnice pomiędzy WASM a JavaScript obejmują:
- WASM jest językiem kompilowanym, natomiast JS jest językiem interpretowanym. Przeglądarka musi pobrać i przeanalizować JavaScript w czasie wykonania, podczas gdy kod WASM jest od razu gotowy do działania dzięki wstępnie skompilowanemu kodowi.
- WebAssembly jest językiem niskopoziomowym. Natomiast JavaScript to język wysokiego poziomu. Dzięki temu JS jest łatwy w obsłudze. Z kolei WASM, będąc językiem niskiego poziomu, może działać znacznie szybciej niż JavaScript.
- JavaScript może poszczycić się dużą i aktywną społecznością. Zatem, jeśli szukasz przyjaznego środowiska programistycznego, JS będzie oczywistym wyborem. WebAssembly natomiast jest stosunkowo nową technologią, stąd brakuje mu zasobów.
Jako programista, nie musisz się zastanawiać, którą technologię wybrać. Wynika to z faktu, że zarówno JS, jak i WASM współpracują ze sobą, zamiast konkurować.
W praktyce, tworząc wysoce wydajną aplikację, możesz użyć WebAssembly do zakodowania jedynie tych fragmentów, które wymagają dużej sprawności. JavaScript API pozwoli Ci na pobranie i wykorzystanie modułów WASM bezpośrednio w kodzie JavaScript.
Podsumowanie
Podsumowując, WebAssembly jest doskonałym uzupełnieniem dla JavaScript. Umożliwia programistom budowanie wydajnych aplikacji, zarówno w środowisku przeglądarki, jak i poza nim. Co istotne, nie ma na celu zastąpienia JavaScript.
Czy WASM przekształci się w kompleksowe rozwiązanie, które wyprze JavaScript? Biorąc pod uwagę założenia WebAssembly, jest to prawdopodobne, choć nie jest to celem samym w sobie. Niemniej jednak, pomysł, by WebAssembly w przyszłości zastąpiło JavaScript, nie jest całkowicie wykluczony.
Na koniec, zachęcamy Cię do zapoznania się z listą najlepszych bibliotek interfejsu użytkownika JavaScript (JS) do tworzenia nowoczesnych aplikacji.
newsblog.pl