Verse の 式 は、immediate か async のいずれかになります。これは、シミュレーションの更新 を基準とした式の 評価 時間を示しています。
シミュレーションの更新は、新しい フレーム が表示されるタイミングと考えてください。
オンライン ゲームがサーバーと同期しなくなった場合など、新しいフレームが表示される前に複数のシミュレーションの更新が行われることがあります。
immediate | async |
---|---|
immediate 式は遅延なく評価を行い、現在のシミュレーションの更新内で評価を完了させます。 | async 式は評価に時間がかかる可能性があり、必ずしも評価する必要はありません。async 式は、現在または後続のシミュレーションの更新で完了することもあれば、完了しないこともあります。 |
async コンテキスト
async 式は、async コンテキスト を持つすべての Verse コードで使用できます。
async コンテキスト は、suspends
エフェクト 指定子 を持つ 関数 の 本体 です。suspends
エフェクトは、async 関数が複数の シミュレーション の更新の完了前にそれらのさまざまなポイントで制御を一時停止し、他の並列処理式に協調的に転送できることを示します。
Verse の仕掛けの OnBegin()
関数は、async コードの開始点として使用される一般的な async 関数です。
async 関数の呼び出しでは、次のように immediate 関数を呼び出す同じ 構文 が使用されます。
OnBegin<override>()<suspends> : void =
HideAllPlatforms()
HideAllPlatforms()<suspends> : void =
for (Platform : Platforms):
Platform.Hide()
Sleep(Delay)
他の式と同じように、async 式は 結果 を返すことができます。async 式の結果は、式が完了した後にしか確認できません。
# Npc は MoveToNearestNPC() が完了した後に結合されるまで未定義であり、数フレーム先になる可能性があります
Npc := Player.MoveToNearestNPC()
#MoveToNearestNPC() が完了してから呼び出されます
Print("Moved to {Npc}")
(async 関数の本体内の) async コンテキスト内にあるコード ブロックには、immediate 式と async 式の組み合わせが含まれていることがあります。
- コード ブロックのいずれかの式が async の場合、コード ブロック全体が async とみなされます。
- コード ブロックのすべての式が immediate の場合、コード ブロック全体が immediate とみなされます。
以下の例では、すべての式が async 式であるため、コード ブロック全体が async となります。
Sleep(2.0) # 2 秒待ちます
Boss.TauntEmote() # TauntEmote() が完了するまで待ちます
Player.MoveToNearestNPC() # MoveToNearestNPC() が完了するまで待ちます
以下の例では、すべての式が immediate 式であるため、コード ブロック全体が immediate となります。
Print("Reset after explosion")
Platform.Show()
set SecondsUntilExplosion = 12.0
以下の例では、async 式と immediate 式が組み合わせられているため、コード ブロック全体が async となります。
Print("Started")
var Seconds := 1.0
Sleep(Seconds)
Print("Waited {Second} seconds")
set Second += 1.0
Sleep(Seconds)
Print("Waited {Second} seconds")
set Second += 1.0
Sleep(Seconds)
Print("Waited {Second} seconds")
immediate 式は独自につながり合います。隣接する (async ではない) immediate 式はすべて アトミック とみなされ、それらのコードは同じ更新内で中断なく、プリエンプション または コンテキスト スイッチ なしで確実に実行されます。これは、このようなコードにアトミック 相互排除 プリミティブ がラップされているかのような動作になります。
そのため上記のようなコードから、これらの immediate 式はアトミックに処理されます。
# これら 2 つの式は常に結び付けられます
Print("Started")
var Seconds := 1.0
Sleep(Seconds)
# これら 2 つの式は常に結び付けられます
Print("Waited {Second} seconds")
set Second += 1.0
Sleep(Seconds)
# これら 2 つの式は常に結び付けられます
Print("Waited {Second} seconds")
set Second += 1.0
Sleep(Seconds)
Print("Waited {Second} seconds")
他のコード ブロックと同じように、async コード ブロックの最後の式は 結果 として使用されます。
並列処理式
Verse では、並列処理式 を使用して 式 を並列で (同時に) 実行する のか順番に次々実行するのかを決定します。async 式は経時的に実行されるか 呼び出される ため、これらの並列処理式は async 式を使用している場合に特に有用です。
構造化並列処理
async 式は実行に時間がかかる場合、他の式が実行されないようにブロックします。たとえば、Sleep(90.0)
を使用するとプログラムが 90 秒待機し、Sleep(90.0)
が完全に実行されるまで次の式がブロックされます。
構造化並列処理 式は、async 論理タイムフローを指定し、(async 関数 本体 などの) 特定の async コンテキストのスコープ に論理的に制約される 有効期限 で async 式のブロッキング性を変更するために使用されます。
これは、関連する スコープ に制約される block
、if
、for
、loop
などの構造化フロー制御と似ています。
Verse の async 式では、他の言語のコルーチン実装で使用される yield
および await
プリミティブ は使用しません。これと同じメカニズムは、Verse 並列処理式と内部メカニズムを使用することで実現されます。
構造化並列処理の詳細については、「Sync」、 「Race」、「Rush」、「Branch」を参照してください。
非構造化並列処理
非構造化並列処理式は、spawn
1 つだけです。この式には、特定の async コンテキストのスコープに論理的に制約されないものの、式が実行されたスコープの範囲を超える可能性がある有効期限があります。
非構造化並列処理は非常口のようなもので日常的に使用すべきではありませんが、最善で唯一の選択肢となることもあります。
構造化並列処理式 (sync
、race
、rush
、branch
) は、可能な限り非構造化並列処理 (spawn
) 式の前に使用する必要があります。
非構造化並列処理の詳細については、「spawn」を参照してください。
現在実行されている async 式をトラックするためのタスク
async 式には タスク が関連付けられています。
タスク は、実行が開始されたものの、別のタスクを完了できるようにするために 一時停止 状態になった async 関数を表す オブジェクト です。
このタスクは、async 式のステータスをチェックし、必要に応じてその async 式をキャンセルするために使用できます。
タスクの詳細については、「タスク」を参照してください。