Async Processing Example
This example demonstrates how to use GDSignalBus's async signal functionality to handle time-consuming operations without blocking the main thread.
Complete Example Code
gdscript
@warning_ignore_start("static_called_on_instance")
extends Node
# Async signal example
# Demonstrates how to use async callbacks to handle time-consuming operations
@onready var bus = SignalBus.get_singleton()
func _ready():
print("\n=== Async Example Started ===")
bus.set_debug_enabled(true)
# Example 1: Basic async subscription
print("Subscribing to async_event...")
bus.subscribe_async("async_event", func(message):
print("[Async Thread] Processing message: ", message)
# Simulate time-consuming operation
OS.delay_msec(3000)
print("[Async Thread] Processing complete")
)
# Example 2: Async subscription with callbacks
var async_callback = func(data):
print("[Async Thread] Starting data processing: ", data)
OS.delay_msec(5000)
return "Processing result: " + str(data * 2)
var complete_callback = func(result):
print("[Main Thread] Processing complete, result: ", result)
var error_callback = func(error):
print("[Main Thread] Processing failed: ", error)
# Subscribe to async signal with completion and error callbacks
print("Subscribing to data_processing...")
bus.subscribe_async_with_callbacks(
"data_processing",
async_callback,
complete_callback,
error_callback
)
print("=== Subscriptions Complete ===\n")
func _process(_delta):
# Process completed async tasks in the main thread
bus.process_async_tasks()
if Input.is_action_just_pressed("ui_up"):
# Emit async signal
print("[Main Thread] Emitting async signal...")
bus.emit_async("async_event", ["Hello Async!"])
print("[Main Thread] Continuing execution (not waiting)")
if Input.is_action_just_pressed("ui_down"):
# Emit signal
print("[Main Thread] Emitting data processing signal...")
bus.emit_async("data_processing", [42])
print("[Main Thread] Continuing execution")
func _exit_tree():
print("\n=== _exit_tree called ===")
print("Active async tasks: ", bus.get_active_async_task_count())
print("=== _exit_tree finished ===\n")Feature Description
1. Basic Async Subscription
gdscript
bus.subscribe_async("async_event", func(message):
print("[Async Thread] Processing message: ", message)
# Simulate time-consuming operation
OS.delay_msec(3000)
print("[Async Thread] Processing complete")
)2. Async Subscription with Callbacks
gdscript
# Define async processing function
var async_callback = func(data):
print("[Async Thread] Starting data processing: ", data)
OS.delay_msec(5000)
return "Processing result: " + str(data * 2)
# Define completion callback
var complete_callback = func(result):
print("[Main Thread] Processing complete, result: ", result)
# Define error callback
var error_callback = func(error):
print("[Main Thread] Processing failed: ", error)
# Subscribe to async signal with callbacks
bus.subscribe_async_with_callbacks(
"data_processing",
async_callback,
complete_callback,
error_callback
)3. Async Signal Emission
gdscript
# Emit async signals
bus.emit_async("async_event", ["Hello Async!"])
bus.emit_async("data_processing", [42])4. Async Task Processing
gdscript
func _process(_delta):
# Process completed async tasks in the main thread
bus.process_async_tasks()Control Instructions
After running the example, you can use the following keys to test async functionality:
- UI Up (Up arrow): Emit
async_eventasync signal, triggering 3-second async processing - UI Down (Down arrow): Emit
data_processingasync signal, triggering 5-second data processing
Output Example
Basic Async Signal Output
[Main Thread] Emitting async signal...
[Main Thread] Continuing execution (not waiting)
[Async Thread] Processing message: Hello Async!
# After 3 seconds
[Async Thread] Processing completeAsync Signal with Callbacks Output
[Main Thread] Emitting data processing signal...
[Main Thread] Continuing execution
[Async Thread] Starting data processing: 42
# After 5 seconds
[Main Thread] Processing complete, result: Processing result: 84Cleanup Output
=== _exit_tree called ===
Active async tasks: 0
=== _exit_tree finished ===Core Concepts
Async Processing Flow
- Subscription Phase: Use
subscribe_async()orsubscribe_async_with_callbacks()to subscribe to async signals - Emission Phase: Use
emit_async()to emit async signals, returns immediately without waiting - Processing Phase: Async thread executes time-consuming operations
- Completion Phase: Calls completion callbacks in the main thread (if provided)
- Cleanup Phase: Process completed async tasks in
_process()
Async vs Sync
- Sync:
emit()blocks until all subscribers complete processing - Async:
emit_async()returns immediately, processing happens in the background
Callback Mechanism
- Async Callback: Executed in async thread, used for time-consuming operations
- Completion Callback: Executed in main thread, used for handling results
- Error Callback: Executed in main thread, used for handling errors
Task Management
gdscript
# Get active async task count
bus.get_active_async_task_count()
# Process completed async tasks (must be called in _process)
bus.process_async_tasks()Best Practices
1. Always Call process_async_tasks()
gdscript
func _process(_delta):
bus.process_async_tasks() # Must be called every frame2. Use Callbacks Appropriately
gdscript
# For simple async operations, use basic subscription
bus.subscribe_async("simple_task", async_handler)
# For operations that need result handling, use subscription with callbacks
bus.subscribe_async_with_callbacks("complex_task",
async_handler,
complete_callback,
error_callback
)3. Avoid Long Blocking Operations
gdscript
# Break long operations into small chunks
func long_running_task(data):
for i in range(100):
process_chunk(data[i])
if i % 10 == 0: # Check every 10 chunks
OS.delay_msec(1) # Yield control4. Error Handling
gdscript
# Always provide error callbacks
var error_callback = func(error):
print("Async operation failed: ", error)
# Implement fallback strategy
bus.subscribe_async_with_callbacks("risky_task",
async_handler,
complete_callback,
error_callback
)This async example demonstrates how to handle time-consuming operations without blocking the main thread, which is an important tool for building responsive game applications.