W Verse wyrażenie może być natychmiastowe lub asynchroniczne. Definiuje to, jak długo może trwać ocena wyrażenia w odniesieniu do aktualizacji symulacji.
Pomyśl o aktualizacji symulacji, gdy wyświetlana jest nowa klatka.
Istnieją przypadki, w których przed nową klatką może wystąpić wiele aktualizacji symulacji, na przykład gdy gra online przestanie być zsynchronizowana z serwerem.
| natychmiastowe | async |
|---|---|
Wyrażenie natychmiastowe dokonuje oceny bez opóźnienia, co oznacza, że ocena zostanie ukończona w ramach bieżącej aktualizacji symulacji. | Wyrażenie asynchroniczne (async) może wymagać czasu na ocenę, ale nie musi. Wyrażenie asynchroniczne może, ale nie musi, zostać ukończone w bieżącej lub późniejszej aktualizacji symulacji. |
Konteksty asynchroniczne
Wyrażenia asynchroniczne mogą być używane w każdym kodzie Verse z kontekstem asynchronicznym.
Kontekst asynchroniczny jest ciałem funkcji zawierającej specyfikator efektu suspends. Efekt suspends wskazuje, że funkcje asynchroniczne mogą zawieszać i wspólnie przekazywać kontrolę do innych współbieżnych wyrażeń w różnych punktach w ciągu kilku aktualizacji symulacji przed ich zakończeniem.
Funkcja OnBegin() w urządzeniu Verse jest powszechną funkcją asynchroniczną używaną jako punkt wyjścia dla kodu asynchronicznego.
Wywołanie funkcji asynchronicznej ma taką samą składnię, jak wywołanie funkcji natychmiastowej:
OnBegin<override>()<suspends> : void =
HideAllPlatforms()
HideAllPlatforms()<suspends> : void =
for (Platform : Platforms):
Platform.Hide()
Sleep(Delay)Jak każde inne wyrażenie, wyrażenie asynchroniczne może mieć wynik. Wynik wyrażenia asynchronicznego jest dostępny dopiero po jego ukończeniu.
# 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}")Każdy blok kodu znajdujący się w kontekście asynchronicznym (wewnątrz ciała funkcji asynchronicznej) może zawierać dowolną kombinację wyrażeń natychmiastowych i asynchronicznych.
Jeśli jakiekolwiek wyrażenia w bloku kodu są asynchroniczne, wówczas cały blok kodu jest uważany za asynchroniczny.
Jeśli wszystkie wyrażenia w bloku kodu są natychmiastowe, wówczas cały blok kodu jest uważany za natychmiastowy.
Wszystkie wyrażenia w poniższym przykładzie są wyrażeniami asynchronicznymi, więc cały blok kodu jest asynchroniczny:
Sleep(2.0) # waits 2 seconds
Boss.TauntEmote() # waits until TauntEmote() completes
Player.MoveToNearestNPC() # waits until MoveToNearestNPC() completesWszystkie wyrażenia w poniższym przykładzie są wyrażeniami natychmiastowymi, więc cały blok kodu jest natychmiastowy:
Print("Reset after explosion")
Platform.Show()
set SecondsUntilExplosion = 12.0Wyrażenia w poniższym przykładzie są mieszanką wyrażeń asynchronicznych i natychmiastowych, więc cały blok kodu jest asynchroniczny:
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
Wyrażenia natychmiastowe trzymają się razem same. Wszystkie sąsiadujące wyrażenia natychmiastowe (nieasynchroniczne) są uważane za niepodzielne – ich kod ma gwarancję nieprzerwanego działania z tą samą aktualizacją i bez wywłaszczenia lub przełączania kontekstu. To tak, jakby taki kod miał automatycznie owinięty wokół siebie prymityw wzajemnego wykluczania.
Tak więc w powyższym przykładzie kodu te natychmiastowe wyrażenia są traktowane jako niepodzielne:
# 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
Jak w przypadku każdego innego bloku kodu, ostatnie wyrażenie w asynchronicznym bloku kodu jest używane jako wynik.
Wyrażenia współbieżności
Verse używa wyrażeń współbieżności do określenia, czy wyrażenia są wykonywane współbieżnie (w tym samym czasie), czy sekwencyjnie, jedno po drugim. Wyrażenie asynchroniczne jest wykonywane lub wywoływane w czasie, więc te wyrażenia współbieżności mogą być szczególnie przydatne, gdy używasz wyrażeń asynchronicznych.
Współbieżność ustrukturyzowana
Wyrażenie asynchroniczne zablokuje wykonywanie innych wyrażeń, jeśli jego wykonanie zajmie dużo czasu. Na przykład użycie Sleep(90.0) spowoduje, że program będzie czekał 90 sekund, blokując następne wyrażenie, dopóki Sleep(90.0) nie zostanie w pełni wykonane.
Wyrażenia współbieżności ustrukturyzowanej służą do definiowania asynchronicznego logicznego przepływu czasu oraz modyfikowania blokującego charakteru wyrażeń asynchronicznych za pomocą czasu życia logicznie ograniczonego do konkretnego zakresu kontekstu asynchronicznego (takiego jak ciało funkcji asynchronicznej).
Działa to podobnie, jak ustrukturyzowaną kontrolę przepływu, na przykład wyrażenia block, if, for czy loop, które wprowadzają ograniczenie do skojarzonego z nimi zakresu.
Wyrażenia asynchroniczne Verse nie używają prymitywów yield i await używanych przez implementacje asynchroniczne w innych językach. Te same mechanizmy są realizowane przy użyciu wyrażeń współbieżności Verse i mechanizmów wewnętrznych.
Więcej informacji o ustrukturyzowanej współbieżności znajdziesz w artykule Sync, Race, Rush i Branch.
Współbieżność nieustrukturyzowana
Istnieje tylko jedno wyrażenie współbieżności nieustrukturyzowanej – spawn. To wyrażenie ma czas życia, który nie jest logicznie ograniczony do konkretnego zakresu kontekstu asynchronicznego, ale potencjalnie może wykraczać poza zakres, w którym zostało wykonane.
Współbieżność nieustrukturyzowana jest jak awaryjny właz ewakuacyjny – nie należy używać jej regularnie, chociaż czasami jest to najlepsza i jedyna opcja.
Wyrażenia współbieżności ustrukturyzowanej (sync, race, rush i branch) powinny być używane przed wyrażeniami współbieżności nieustrukturyzowanej (spawn), gdy tylko jest to możliwe.
Więcej informacji o współbieżności nieustrukturyzowanej znajdziesz w artykule Spawn.
Zadania śledzenia aktualnie wykonywanych wyrażeń asynchronicznych
Z wyrażeniem asynchronicznym jest powiązane zadanie.
Zadanie (task) jest obiektem reprezentującym funkcję asynchroniczną, która rozpoczęła wykonywanie, ale została zawieszona, aby umożliwić wykonanie innego zadania.
Zadanie może być użyte do sprawdzenia statusu wyrażenia asynchronicznego i anulowania wyrażenia asynchronicznego, jeśli jest to pożądane.
Więcej informacji na temat zadań znajdziesz w artykule Task.