Futures

Futures in ExEngine represent the outcome of asynchronous operations. They provide a way to handle long-running tasks without blocking the main execution thread. Futures allow you to submit events for execution and then either wait for their completion or continue with other tasks, checking back later for results. This enables efficient, non-blocking execution of complex workflows.

When you submit an event to the ExecutionEngine, it returns a future:

from exengine import ExecutionEngine
from exengine.events import SomeEvent

engine = ExecutionEngine.get_instance()
event = SomeEvent()
future = engine.submit(event)

You can then use this future to interact with the ongoing operation.

Waiting for Completion + Error Handling

To wait for an event to complete and get its result:

result = future.await_execution()

This will block until the event completes.

If an event raises an exception during its execution, you can catch it when awaiting the future:

try:
    result = future.await_execution()
except Exception as e:
    print(f"Event failed with error: {e}")

Checking Completion Status

You can check if an event has completed without blocking:

if future.is_execution_complete():
    print("Event has completed")
else:
    print("Event is still running")

Notifications

Futures can be used to await specific notifications from an event:

future.await_notification(SomeSpecificNotification)

This will block until the specified notification is received. If the notification has already been received when this method is called, it will return immediately.

Retrieving Data

For data-producing events, use the future’s await_data method to retrieve data:

# Retrieve a single piece of data
data, metadata = future.await_data({'time': 2}, return_data=True, return_metadata=True)

# Retrieve multiple pieces of data
data_list = future.await_data([{'time': 0}, {'time': 1}, {'time': 2}], return_data=True)

The data_coordinates parameter can specify a single piece of data or multiple pieces.

Stopping Execution

If an event is stoppable (inherits from Stoppable), you can use the future to stop it:

future.stop(await_completion=True)

This requests the event to stop its execution. The await_completion parameter determines whether the method should block until the event has stopped.

Aborting Execution

Similarly, for abortable events (inheriting from Abortable):

future.abort(await_completion=True)

This immediately terminates the event’s execution. As with stop, await_completion determines whether to wait for the abortion to complete.