Return signal and emit at the same time

Godot Version

4.2.1

Question

Is it possible to return a “Signal” that immediately emits data?

I have a global singleton that loads data asynchronously, returning a signal that can be awaited to get the data when it is loaded. See below for an example of how this works.

the singleton code:

class Job:
     name: String
     signal loaded
     func _init(name):
          self.name = name

func load_data_async(name):
     # Encapsulate the work to be done in an inner class
     var job = MyJob(name)
     # Send the job to a thread pool, which will emit job.loaded when the data is loaded
     add_job_to_threadpool(job)
     # Return the signal which can be awaited or connected to a slot
     return job.loaded

usage somewher else:

var data = await MySingleton.load_data_async(data_name)

This works well. However, I want my singleton to cache data, and if the data is already loaded, immediately return the data rather than sending it to a thread pool. Something like the below:

var cache = {}
func load_data_async(name):
     if name in cache:
          # Return a signal containing the data, but that immediately emits the data
          return ?

     var job = MyJob(name)
     # The data will be loaded in a thread here, but also added to the cache for subsequent use
     add_job_to_threadpool(job)
     return job.loaded

func example_usage():
     var loaded = MySingleton.load_data_async(data_name)
     loaded.connect(_on_data_loaded)

func _on_data_loaded(data):
     ...

Any ideas? Thanks!

I don’t think that’s possible. What you can do is await for the loaded signal inside the load_data_async() function if it’s not in the cache and return the cache contents if it’s already available.

Something like:

var cache = {}


func load_data_async(name:String) -> void:
	# If not in the cache
	if not name in cache:
		# Create a job
		var job = MyJob(name)
		add_job_to_threadpool(job)
		
		# Await for its loaded signal
		await job.loaded
		# Add the data to the cache
		cache[name] = job.data
		
	return cache[name]
	
	
func example_usage() -> void:
	var data = await load_data_async(data_name)
	print(data)
2 Likes

You can pass a callable parameter in your load_data_async instead of using the return of a signal :

func load_data_async(name : String, call : Callable):

	if (cache.has(name)):
		call.call(cache[name])
		return;

	# Encapsulate the work to be done in an inner class
	var job = Job.new(name)
	# Assign callable to the signal
	job.loaded.connect(call);
	# Send the job to a thread pool, which will emit job.loaded when the data is loaded
	add_job_to_threadpool(job)

func example_usage():
	load_data_async("test1", _loaded);


func _loaded(data):
	print(data)
2 Likes

Thanks, both are good solutions!

2 Likes