提供: Japanese Scratch-Wiki
Dispatchは、拡張機能とScratchの仮想マシン間で利用される双方向リモート・プロシージャ・コールの名前である。Dispatchは主にサンドボックス化された拡張機能への通信のために実装されているが、実際はサンドボックス化されていない、組み込みの拡張機能への通信もDispatchを経由している。サンドボックス化された拡張機能への通信には、postMessage関数が利用されている。
Dispatchを実装するクラスは、2種類存在する:
- CentralDispatch - 仮想マシン側。シングルトンである。
- WorkerDispatch - 拡張機能側。拡張機能あたり1つ読み込まれる。
サービス
Dispatchのメッセージを送受信するものは、「サービス」と呼ばれている。Scratch 3.0の実装では、ランタイム(サービス名runtime
)、拡張機能マネージャー(サービス名extensions
)、そして各拡張機能のWorker(サンドボックス化されていない場合、拡張機能のクラスインスタンス)がサービスとして登録されている。サービスは、Dispatchを実装するクラスのインスタンスを通じ別のサービスにメッセージを送信することにより、相手側に定義されたメソッドを実行する。メッセージを受信したサービスは、メッセージに含まれるメソッド名と引数を利用してメソッドを実行し、返り値を相手に送り返す。
Workerへのメソッド実行と、Workerからのメソッド実行は、非同期的に行わなければならないが、仮想マシン側同士が利便性のためDispatchを利用してメソッドを実行している場合は、同期的に実行することも可能である。
メソッドの一覧
以下に、実際に呼び出されるメソッドの一覧を示す。もちろん、これ以外のメソッドも、サービスに属する限り呼び出すことができる。
呼び出し元サービス | 呼び出し元メソッド | 宛先サービス | 宛先メソッド | 目的 |
---|---|---|---|---|
ExtensionManager | registerExtensionServiceSync | 拡張機能のインスタンス | getInfo | サンドボックス化されていない拡張機能の登録時に、拡張機能に関する情報を取得する。このため、「Sync」(同期)とある。 |
ExtensionManager | registerExtensionService | ExtensionWorker | getInfo | サンドボックス化されている拡張機能の登録時に、拡張機能に関する情報を取得する。 |
ExtensionManager | refreshBlocks | ExtensionWorker | getInfo | 表示更新時に、拡張機能に関する情報を取得する。 |
ExtensionManager | refreshBlocks | Runtime | _refreshExtensionPrimitives | ランタイムに、拡張機能が登録するブロック(Primitive)の情報を更新するよう伝える。 |
ExtensionManager | _registerInternalExtension | ExtensionManager | registerExtensionServiceSync | サンドボックス化されていない拡張機能を登録する。 |
ExtensionManager | _registerExtensionInfo | Runtime | _registerExtensionPrimitives | ランタイムに、拡張機能のブロック(Primitive)を登録するよう伝える。 |
ExtensionWorker | constructor | ExtensionManager | allocateWorker | 起動したばかりのExtensionWorkerから拡張機能マネージャーに、どのURLを読み込むかの情報を要求する。 |
ExtensionWorker | constructor | ExtensionManager | onWorkerInit | Workerの準備が完了したことを伝える。 |
ExtensionWorker | register | ExtensionManager | registerExtensionService | 拡張機能を登録する。 |
メッセージ
Dispatchメッセージは、以下の5種類である。以下のメッセージが、postMessage
関数経由で送信される。
呼び出し
基本のメッセージで、サービスのメソッドを実行する。
{
"service": "extension_0_scratch3_pen",
"method": "getInfo",
"args": []
}
キー | 値 |
---|---|
service |
サービス名 |
method |
メソッド名 |
args |
引数を含む配列 |
返り値
メソッドの返り値を送信する。
{
"responseId": 0,
"error": null,
"result": 42
}
キー | 値 |
---|---|
responseId |
レスポンスID (数値) |
error |
エラーを示す文字列。成功の場合null
|
result |
返り値 |
サービスの登録
Workerからサービスを登録する。
{
"service": "dispatch",
"method": "setService",
"args": ["extension.0.hogehoge"]
}
キー | 値 |
---|---|
service |
dispatch
|
method |
setService
|
args |
サービス名を含む配列 |
ハンドシェイク
Workerへ接続できたことを確認するために、CentralDispatchが送信する。
{
"service": "dispatch",
"method": "handshake",
"args": []
}
キー | 値 |
---|---|
service |
dispatch
|
method |
handshake
|
args |
空の配列 |
切断
Workerが切断される前に、CentralDispatchが送信する。実際は、利用されていない。
{
"service": "dispatch",
"method": "terminate",
"args": []
}
キー | 値 |
---|---|
service |
dispatch
|
method |
terminate
|
args |
空の配列 |
使用方法
仮想マシン側では/src/dispatch/central-dispatch.js
から読み込めるCentralDispatchを利用し、Worker側ではworker-dispatch.js
(または互換のコード)を利用する。Dispatchにより非同期的にメソッドを実行するには、次のように書く:
// サービス名 extension.0.hoge の returnAnswerToLifeUniverseEverything メソッドを実行し、返り値を出力する
dispatch.call('extension.0.hoge', 'returnAnswerToLifeUniverseEverything').then(result => console.log(result)); // 42
サービス側:
class HogeService {
constructor () {
// サービスを登録する
dispatch.setService('extension.0.hoge', this);
}
returnAnswerToLifeUniverseEverything () {
return 42;
}
}
引数がある場合は:
// サービス名 extension.0.hoge の addTwo メソッドを実行し、返り値を出力する
dispatch.call('extension.0.hoge', 'addTwo', 40).then(result => console.log(result)); // 42
サービス側:
class HogeService {
constructor () {
// サービスを登録する
dispatch.setService('extension.0.hoge', this);
}
addTwo (i) {
return i + 2;
}
}