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 is undefined until it is bound after MoveToNearestNPC() completes which may be several frames into the future
Npc := Player.MoveToNearestNPC()
#Only called after MoveToNearestNPC() completes
Print("Moved to {Npc}")(async 関数の本体内の) async コンテキスト内にあるコード ブロックには、immediate 式と async 式の組み合わせが含まれていることがあります。
コード ブロックのいずれかの式が async の場合、コード ブロック全体が async とみなされます。
コード ブロックのすべての式が immediate の場合、コード ブロック全体が immediate とみなされます。
以下の例では、すべての式が async 式であるため、コード ブロック全体が async となります。
Sleep(2.0) # waits 2 seconds
Boss.TauntEmote() # waits until TauntEmote() completes
Player.MoveToNearestNPC() # waits until MoveToNearestNPC() completes以下の例では、すべての式が 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
immediate 式は独自につながり合います。 この場合、隣接する (async ではない) immediate 式はすべてアトミックとみなされ、それらのコードは同じ更新内で中断なく、プリエンプションまたはコンテキスト スイッチなしで確実に実行されます。 これは、このようなコードにアトミック相互排他プリミティブがラップされているかのような動作になります。
そのため上記のようなコードから、これらの immediate 式はアトミックに処理されます。
# These two expressions are always kept together
Print("Started")
var Seconds := 1.0
Sleep(Seconds)
# These two expressions are always kept together
Print("Waited {Second} seconds")
set Second += 1.0
他のコード ブロックのように、async コード ブロックの最後の式は結果として使用されます。
並列処理式
Verse では、並列処理式を使用して式を並列で (同時に) 実行するのか順番に次々実行するのかを決定します。 async 式は経時的に実行されるか呼び出されるため、これらの並列処理式は async 式を使用している場合に特に有用です。
構造化並列処理
async 式は実行に時間がかかる場合、他の式が実行されないようにブロックします。 たとえば、Sleep(90.0) を使用すると、プログラムが 90 秒待機し、Sleep(90.0) が完全に実行されるまで次の式がブロックされます。
構造化並列処理式は、async 論理タイムフローを指定し、(async 関数本体などの) 特定の async コンテキストのスコープに論理的に制約される有効期限で async 式のブロッキング性を変更するために使用されます。
これは、関連するスコープに制約される block、if、for、loop などの構造化フロー制御と似ています。
Verse の async 式では、他の言語の async 実装で使用される yield および await プリミティブは使用しません。 これと同じメカニズムは、Verse 並列処理式と内部メカニズムを使用することで実現されます。
構造化並列処理の詳細については、「sync」、 「race」、「rush」、「branch」を参照してください。
非構造化並列処理
非構造化並列処理式は、spawn 1 つだけです。 この式には、特定の async コンテキストのスコープに論理的に制約されないものの、式が実行されたスコープの範囲を超える可能性がある有効期限があります。
非構造化並列処理は非常口のようなもので日常的に使用すべきではありませんが、最善で唯一の選択肢となることもあります。
構造化並列処理式 (sync、race、rush、branch) は、可能な限り非構造化並列処理 (spawn) 式の前に使用する必要があります。
非構造化並列処理の詳細については、「spawn」を参照してください。
現在実行されている async 式をトラックするためのタスク
async 式にはタスクが関連付けられています。
タスクは、実行が開始されたものの、別のタスクを完了できるようにするために一時停止状態になった async 関数を表すオブジェクトです。
このタスクは、async 式のステータスをチェックし、必要に応じてその async 式をキャンセルするために使用できます。
タスクの詳細については、「タスク」を参照してください。