Await All in Godot 4

Godot 4 introduced the await keyword to pause function execution. It can be used with signals or other functions. Execution will resume when the signal is emitted or the function returns (Note that the function might contain its own await calls).

func await_confirmation():
	await $Button.pressed
	$Modal.hide()


func await_modal_hide():
	await await_confirmation()
	# Do something after the modal is hidden

In various situations, I needed to pause function execution until several asynchronous conditions were satisfied. An example is waiting for a list of objects to finish animating, with each object having its unique animation duration and conditions.

Below is my solution to that problem:

# This needs to be added as an auto-load (I used the global name `Co`)
class_name Coroutines
extends Node

func await_all(list: Array):
	var counter = {
		value = list.size()
	}
	
	for el in list:
		if el is Signal:
			el.connect(count_down.bind(counter), CONNECT_ONE_SHOT)
		elif el is Callable:
			func_wrapper(el, count_down.bind(counter))
	
	while counter.value > 0:
		await get_tree().process_frame


func count_down(dict):
	dict.value -= 1


func func_wrapper(call: Callable, call_back: Callable):
	await call.call()
	call_back.call()
# Usage example
func do_something():
	await Co.await_all([obj1.animated, obj2.execute, obj3.pressed])
	# Continue execution after all signals emit and functions return 

This script must be added as an Autoload in Godot since it relies on the SceneTree.process_frame signal. My attempts to implement the function without this requirement led to issues where func_wrapper did not return as expected.

The await_all function can accept a list containing either Signals or Callables. The function in the Callable will be immediately executed, and its return awaited.

Published 2023.10.06

Want to know when I release a new game?