glooを理解する
gloo
WebAssemblyの開発を楽にするライブラリ、
これからわかったことを書いていきます
glooがやりやすくしてくれることの一覧
ざっくりいうと
web-sys
やjs-sys
で書きにくいようなものを簡単にしてくれるいいライブラリ!
対応してくれるもの
- console(開発者ツールのコンソールへの表示)
- dialog(多分確認表示のときに出すもの)
- events(イベントリスナーを使うときに簡単にするやつ)
- これが一番助かりそう...!
- blobs(ファイルや
blob
を使う時に使うらしい) - history(ユニバーサルセッション履歴?と位置情報を扱うためのもの)
- ユニバーサルセッション履歴ってのがわからん、後で調べる
- net(HTTPRequestを扱うもの、fetchとかWebSocketを扱う)
- render(requestAnimationFrameのラッパー)
- WebGL関連、応用としてWebGPU,WebXRに関連する
- storage(Web Storageに関連する)
- そんなに使うか...?
- timers(SetTimeoutとかsetInterval)
- utils
- なんだろうこれ
- worker(WebWorker)
Example
- clock
- file-hash
- history-wasi
- markdown
- prime
ちょっとWorkerを使ってみたい
普通にやってみるとどうなるの?
[package]
name = "example"
version = "0.1.0"
edition = "2021"
+ [lib]
+ crate-type = ["cdylib"]
[dependencies]
+ wasm-bindgen = "0.2.92"
console_error_panic_hook = { version = "0.1.6", optional = true }
+ [dependencies.web-sys]
+ version = "0.3.4"
+ features = [
+ 'console',
+ 'Document',
+ 'HtmlElement',
+ 'HtmlInputElement',
+ 'MessageEvent',
+ 'Window',
+ 'Worker',
+ ]
web-sys
におけるworker
の扱いから理解していきたい
まず、まず、WorkerのClassがある。
色々なメソッドがあるのがわかるが、いったん必要なのは
以下の5つほどだと考えられるが
- new
- onmessage
- set_onmessage
- post_message
- terminate
個人的にはこっち選択した方が書きやすい気もする
- new
-
onmessage-> add_event_listener_with_event_listener - post_message
- terminate
add_event_listener_with_event_listener
なのか?
なぜ書いててわかりやすかった
既存のWebWorkerコードを置換していく
まず、JavaScriptで書かれたコード
const MyWorker = new Worker('worker.js');
const OutputElement = document.getElementById('output');
const buttonEl = document.createElement('button');
buttonEl.textContent = 'worker!';
buttonEl.addEventListener('click', (event) => {
event.preventDefault();
MyWorker.postMessage('test');
setTimeout(() => {
MyWorker.terminate();
OutputElement.textContent = 'Worker Terminate';
}, 2500);
});
MyWorker.onmessage = (event) => {
// console.log(event.data);
OutputElement.textContent = event.data;
};
document.getElementById('main').appendChild(buttonEl);
self.addEventListener('message', (event) => {
postMessage(event.data + ' in Worker!');
});
下記が参考になりそうだったので、これらからわかることをまとめていく
Fetch Do Code Reading
use gloo_net::http::RequestModeがCorsの制御をする補助をしてくれているように見える
use gloo_net::http::RequestがHTTPメソッドを使ったデータフェッチの補助をするものであるように見える
なんかHonoのappのHTTPメソッドを選ぶやつをみている感覚になった
でもRequestインスタンスを作成している方が近いのだろうか、どちらにせよ書きやすくなっている
Example Reading
- clock
- spawn_localを非同期で実行してる
- その中で、glooのIntervalStreamで一秒ごとの実行による変更を行っている
- file-hash(必要だと思った部分だけ見てる)
- gloo_workerにまとめられている
- HandlerId,Worker,WorkerScopeが重要なところを占めているのではないか?
- gloo_workerにまとめられている
- markdown
- Workerの使い方がわかるっぽい。これを読むのが早そう
- use gloo::worker::oneshot::oneshot;というのがある。一回きりのWorker作成か?
- また、Spawnableというのが出てきた。
- Worker::spawner().spawn(worker_file_url)という関数でWorker生成している?
- use gloo::worker::Registrable;というのも出てきた
- Woker登録?Worker::registrar().register();があるから登録しているんだろうけど、どこへ?
- prime
- READMEすらない。なんなんだ。
- use gloo::worker::reactor::{reactor, ReactorScope};はなんなんだ。
- Reactorってことはどういうことなん?
まともなサンプルないの??
なさそうだった(コメントがあんまないし、どこを何を担当しているのかは大体わかったけど、サンプルみて、「ああ!そういうことか!」ってなるのがない)ので、Docを読むことにした
gloo_workerのDocを読む
- 使用方法は2つあるらしい
- Workerトレイトを使う
- #[oneshot]か#[reactor]マクロを使う
- この2つのマクロの違いは
- ONESHOTは各入力が1つの出力を生成する(入力と出力が一対一?)
- Reactorは入力から出力を生成する(???)
- わからんかったので原文を載せて考える
- この2つのマクロの違いは
The macros provide a function-like syntax to spawn workers and communicate with them. There are two macros:
#[oneshot] - Worker where each input produces a single output.
#[reactor] - Worker that receives input(s) and may produce output(s).
Modules Reading
- oneshot 1つのインプットに対して1つのアウトプットが生み出される
- reactor : 多くのインプットを消費し、多くのアウトプットを生み出すことができる
詳しく読む前
Structs Reading
- Bincode
- メッセージエンコーディングらしい。多分、ワーカー間で送受されるメッセージという値のエンコーディングのための機能なのかな?
- HandlerId
- 出力をブリッジに送る識別子らしい。
- ブリッジってなんだよ
- 識別子ってなんだ?IDのことか?ワーカーのIDのことなのか?
- 出力をブリッジに送る識別子らしい。
- WorkerBridge
- コンポーネントとワーカーの間の接続管理をしてくれる
- WorkerDestroyHandle
- ワーカーを落としたら閉じてくれるハンドル
- ワーカーを落とすってなんだよ
- dropがなんの意味を指すかわからない、失敗した時の処理ということ?
- ワーカーを落とすってなんだよ
- ワーカーを落としたら閉じてくれるハンドル
- WorkerRegister
- ワーカーを登録する
- JavaScriptだとワーカー作ったら勝手に登録されるけど、この場合はそうでないのか?
- ワーカーを登録する
- WorkerScope
- コンポーネントとグローバルスケジューラとの参照を保持
- グローバルスケジューラってなんだよ
- コンポーネントとグローバルスケジューラとの参照を保持
- WorkerSpawner
- ワーカーを生成するためのスポナー
- マイクラのスポナーを考えるとわかりやすいかも
- これ(クラスのような動きをするもの)からワーカーを生成しますよ!っていう扱いを想定している?
- マイクラのスポナーを考えるとわかりやすいかも
- ワーカーを生成するためのスポナー
Traits Reading
- Codec
- メッセージをエンコードしたりデコードするフォーマット
- なんかWeb Transportとかそういうやつで面白いことができそうな気もする
- 何かしらの画像を文字列としてエンコードして、文字列から画像としてデコードする的な?
- メッセージをエンコードしたりデコードするフォーマット
- Registrable
- 公開ワーカーをWeb Workerとして登録できるようにするもの
- 公開ワーカーってのはなんなんだろう
- 公開ワーカーをWeb Workerとして登録できるようにするもの
- Spawnable
- スポナーによってスポーンできるワーカー
- これを使ってワーカーを定義して、これに付属したスポーン関数で生成するのか?
- スポナーによってスポーンできるワーカー
- Worker
- Workerの動作を宣言したもの
- Workerの動きそのものはここで定義されている?
- 特定のことだけをする専任のWorkerクラスを作りたい時のインターフェースとして活用する?
- Workerの動きそのものはここで定義されている?
- Workerの動作を宣言したもの
詳しく調べていこう!
Modules
oneshot
サンプルコード
use gloo_worker::oneshot::oneshot;
use gloo_worker::Spawnable;
#[oneshot]
async fn Squared(input: u32) -> u32 {
input.pow(2)
}
let mut squared_bridge = Squared::spawner().spawn("...");
assert_eq!(squared_bridge.run(2).await, 4)
- 謎の点はなんなんや
- 何書くかはせめある程度は示しといてくれ
reactor
サンプル
use gloo_worker::reactor::{reactor, ReactorScope};
use gloo_worker::Spawnable;
use futures::{sink::SinkExt, StreamExt};
#[reactor]
async fn SquaredOnDemand(mut scope: ReactorScope<u64, u64>) {
while let Some(m) = scope.next().await {
if scope.send(m.pow(2)).await.is_err() {
break;
}
}
}
let mut bridge = SquaredOnDemand::spawner().spawn("...");
bridge.send_input(2);
assert_eq!(bridge.next().await, Some(4));
assert_eq!(bridge.next().await, None);
- ここも謎の点々がある
- いいかげんこれがなんなんかおしえてくれ
謎の点々の仮説
WebAssemblyのビルド時に出てくるJavaScriptファイルじゃないかと推測
- サンプルみたらjsコードを読み込んでいた。(
example_prime_worker.js
)- ファイル名が、
src/bin
ディレクトリ内のexample_prime_worker.rs
のファイル名と一致しているため、こいつをビルドしたものを参照しているとみられる。
- ファイル名が、
ビルド方法がわからん!!
今まで複数ファイルにまたがるWebAssemblyをビルドしたことがなかった
マルチスレッドにさせたかった...
流石にこれ以上気力がおきなかった