Une expression dans Verse peut être soit immédiate, soit asynchrone. Tout dépend du temps que met une expression pour évaluer par rapport aux mises à jour de simulation.
Considérez la mise à jour d'une simulation comme le moment où un nouveau cadre est affiché.
Dans certains cas, plusieurs mises à jour de simulation peuvent se produire avant une nouvelle image, par exemple si un jeu en ligne n'est plus synchronisé avec le serveur.
immédiat | asynchrone |
---|---|
Une expression immédiate s'évalue sans tarder, ce qui signifie que l'évaluation sera terminée dans la mise à jour de la simulation actuelle. | Une expression asynchrone peut s'évaluer dans le temps, si cela est nécessaire. Une expression asynchrone peut se terminer ou non lors de la mise à jour actuelle de la simulation ou lors d'une mise à jour ultérieure. |
Contextes asynchrones
Les expressions asynchrones peuvent être utilisées dans tout code Verse qui a un contexte asynchrone.
Un contexte asynchrone est le corps d'une fonction qui a un spécificateur d'effet suspends
. L'effet suspends
indique que les fonctions asynchrones peuvent suspendre et transférer de manière coopérative le contrôle à d'autres expressions concurrentes à divers moments au cours de plusieurs mises à jour de simulation avant de se terminer.
Les fonctions asynchrones sont également appelées coroutines. Ces termes sont utilisés de manière interchangeable.
La fonction OnBegin()
dans un appareil Verse est une fonction asynchrone commune utilisée comme point de départ du code asynchrone.
La syntaxe d'appel d'une fonction asynchrone est la même que celle d'une fonction immédiate :
OnBegin<override>()<suspends> : void =
HideAllPlatforms()
HideAllPlatforms()<suspends> : void =
for (Platform : Platforms):
Platform.Hide()
Sleep(Delay)
Comme toute expression, les expressions asynchrones peuvent avoir un résultat. Le résultat d'une expression asynchrone est seulement disponible une fois qu'elle est terminée.
# Le PNJ est indéfini jusqu'à ce qu'il soit lié après la fin de MoveToNearestNPC(), ce qui peut prendre plusieurs images
Npc := Player.MoveToNearestNPC()
#Appelé uniquement après la fin de MoveToNearestNPC()
MyLog.Print("Moved to {Npc}")
Tout bloc de code qui se trouve dans un contexte asynchrone (à l'intérieur du corps d'une fonction asynchrone) peut avoir n'importe quel mélange d'expressions immédiates et asynchrones.
- Si toutes les expressions d'un bloc de code sont asynchrones, alors l'ensemble du bloc de code est considéré comme étant asynchrone.
- Si toutes les expressions d'un bloc de code sont immédiates, alors l'ensemble du bloc de code est considéré comme immédiat.
Dans l'exemple ci-dessous, toutes les expressions sont asynchrones ; l'ensemble du bloc de code est donc asynchrone :
Sleep(2.0) # attend 2 secondes
Sleep(1.0) # attend 1 seconde
Sleep(3.0) # attend 3 secondes
Dans l'exemple ci-dessous, toutes les expressions sont immédiates ; l'ensemble du bloc de code est donc immédiat :
MyLog.Print("Hey")
MyLog.Print("Salut")
MyLog.Print("Bonjour")
Dans l'exemple ci-dessous, toutes les expressions sont un mélange d'immédiates et d'asynchrones ; l'ensemble du bloc de code est donc asynchrone :
MyLog.Print("Démarré")
var Seconds := 1.0
Sleep(Seconds)
MyLog.Print("Waited {Second} seconds")
set Second += 1.0
Sleep(Seconds)
MyLog.Print("Waited {Second} seconds")
set Second += 1.0
Sleep(Seconds)
MyLog.Print("Waited {Second} seconds")
Les expressions immédiates restent groupées de façon autonome. Toutes les expressions immédiates adjacentes (non asynchrones) sont considérées comme atomiques – leur code est sûr de s'exécuter sans interruption dans la même mise à jour et sans préemption ni changement de contexte. C'est comme si ces codes étaient enveloppés d'une primitive d'exclusion mutuelle automatique.
Ainsi, dans le code ci-dessus, ces expressions immédiates sont traitées de manière atomique :
# Ces deux expressions sont toujours gardées ensemble
MyLog.Print("Started")
var Seconds := 1.0
Sleep(Seconds)
# Ces deux expressions sont toujours gardées ensemble
MyLog.Print("Attendu {Second} secondes")
set Second += 1.0
Sleep(Seconds)
# Ces deux expressions sont toujours gardées ensemble
MyLog.Print("Attendu {Second} secondes")
set Second += 1.0
Sleep(Seconds)
MyLog.Print("Waited {Second} seconds")
Comme tout autre bloc de code, la dernière expression d'un bloc de code asynchrone est utilisée comme résultat.
Expressions de concurrence
Verse utilise des expressions de concurrence pour déterminer si les expressions s'exécutent de manière concurrente (simultanée) ou en séquence, l'une après l'autre. Les expressions asynchrones sont exécutées ou appelées dans le temps, ce qui fait qu'elles peuvent être très utiles lorsque vous utilisez des expressions asynchrones.
Concurrence structurée
Une expression asynchrone empêchera l'exécution des autres expressions si elles prennent du temps. Par exemple, utiliser Sleep(90.0)
causera un arrêt du système de 90 secondes, empêchant la prochaine expression de s'exécuter jusqu'à la fin de Sleep(90.0)
.
Les expressions de concurrence structurée sont utilisées pour spécifier le flux temporel logique asynchrone et pour modifier la nature bloquante des expressions asynchrones dont la durée de vie est logiquement limitée à une étendue de contexte asynchrone spécifique (tel qu'un corps de fonction asynchrone).
Cela s'apparente à un contrôle de flux structuré (tel que block
, if
, for
et loop
) qui reste contraint à l'étendue associée.
Les expressions asynchrones de Verse n'utilisent pas les primitives yield
et await
utilisées par les implémentations de coroutine dans d'autres langues. C'est en utilisant les expressions de concurrence de Verse et les mécanismes internes que les mêmes mécanismes sont réalisés.
Pour en savoir plus sur la concurrence structurée, consultez Sync, Race, Rush et Branch.
Concurrence non structurée
Il n'existe qu'une seule expression concurrente non structurée : spawn
. Cette expression a une durée de vie qui n'est pas logiquement limitée à une étendue de contexte asynchrone spécifique, mais qui peut s'étendre au-delà de celle où elle a été exécutée.
La concurrence non structurée est comme une sortie de secours : elle n'est pas faite pour qu'on s'en serve tous les jours, mais elle sera parfois votre seul recours (et vous serez bien contents qu'elle existe) !
Les expressions de concurrence structurée (sync
, race
, rush
et branch
) doivent être préférées aux expressions de concurrence non structurées (spawn
) chaque fois que cela est possible.
Pour en savoir plus sur la concurrence structurée, consultez Spawn,
Tâches pour le suivi du site qui exécute actuellement les expressions asynchrones
Une expression asynchrone est associée à une tâche.
Une tâche est un objet qui représente une fonction asynchrone en cours d'exécution, mais qui a été suspendue pour qu'une autre tâche se termine.
La tâche peut être utilisée pour vérifier le statut d'une expression asynchrone et pour l'annuler.
Pour en savoir plus sur les tâches, consultez Tâche.