Skip to main content

Events and Async Flow

Events and Async Flow

So far, we’ve focused on how actions are scheduled and composed. In this section, we’ll look at how actions communicate with the rest of the game.

Actions don’t run in isolation — they participate in Excalibur’s event system and can also be used with Promises and async / await to create linear, readable game logic.

Actions and the Event System

As actions run, they emit events through the actor they are attached to.

These events allow other parts of your game to:

  • Observe when actions start or complete
  • React to changes in behavior
  • Interrupt or replace running actions

This keeps actions decoupled from higher-level game logic.

Common Action Events

Actions emit lifecycle events during execution.

The two action events are:

  • Action Start – when an action begins running
  • Action Complete – when an action finishes successfully

These events are dispatched on the actor running the action.

ts
actor.on('actioncomplete', (evt) => {
console.log('An action completed:', evt.action);
});
ts
actor.on('actioncomplete', (evt) => {
console.log('An action completed:', evt.action);
});
note

Action events fire per-action, even when actions are part of a sequence or parallel group.

The ActionEvent Object

When an action-related event fires, Excalibur passes an ActionEvent object to the event handler.

This event object provides context about:

  • Which action triggered the event
  • Which entity was running it
ts
actor.on('actioncomplete', (evt: ActionEvent) => {
console.log(evt.action);
});
ts
actor.on('actioncomplete', (evt: ActionEvent) => {
console.log(evt.action);
});

The ActionEvent is your primary way to connect action execution back to gameplay logic.

Using Promises with Actions

There is a utility method on the actionContext and actionComponent, toPromise().

This let's you manage a returned promise that resolves with either the action or the queue overall clears.

ts
// Run an action and convert its lifecycle into a Promise
await actor.actions
.moveTo(400, 200, 100)
.toPromise();
// This line runs only after the move action completes
console.log('Move finished!');
ts
// Run an action and convert its lifecycle into a Promise
await actor.actions
.moveTo(400, 200, 100)
.toPromise();
// This line runs only after the move action completes
console.log('Move finished!');

Or you can use this with the .then API.

ts
actor.actions
.moveTo(100, 100, 50)
.toPromise()
.then(() => console.log('Done!'));
actor.actions.clearActions();
// The Promise still resolves
ts
actor.actions
.moveTo(100, 100, 50)
.toPromise()
.then(() => console.log('Done!'));
actor.actions.clearActions();
// The Promise still resolves

Demonstration

In the Custom Actions live example, we demonstrate how to use the action events