Skip to content
Version: XState v5

Promise actors

Promise actors are actors that represent a promise that performs some asynchronous task. They may resolve with some output, or reject with an error.

Promise actor capabilities

CapabilityNotes
Receive eventsPromise actors currently do not receive events.
Send eventsPromise actors can send events to other actors it has reference to, such as those provided in its input.
Spawn actorsPromise actors currently cannot spawn new actors.
InputYou can provide input to promise actors.
OutputPromise actors can produce output, which is the resolved value of the promise.

Promise actor logic

You can define promise actor logic using the fromPromise(...) actor logic creator, which takes a function that returns a promise and returns actor logic that can be used to create promise actors.

import { fromPromise, createActor } from 'xstate';

async function getUser(id: string) {
// ...
return { id, /* other user data */ };
}

const promiseLogic = fromPromise(async () => {
const user = await getUser('123');

return user;
});

const promiseActor = createActor(promiseLogic);

promiseActor.subscribe(snapshot => {
console.log(snapshot.status, snapshot.output);
});

promiseActor.start();

// logs 'active', undefined
// ... (after some time)
// logs 'done', { id: '123', /* other user data */ }

Promise actor input

You can pass in input to a promise actor by passing it to the createActor(...) function as the input property of the second argument. In the promise logic (fromPromise(promiseFn)), you read the input property of the first argument passed to the promise function:

import { fromPromise, createActor } from 'xstate';

const promiseLogic = fromPromise(async ({ input }) => {
const user = await getUser(input.id);

return user;
});

const promiseActor = createActor(promiseLogic, {
input: { id: '123' }
});

The input type is inferred from the promise function's first argument type. You can also provide an explicit input type in the second generic parameter:

import { fromPromise } from 'xstate';

interface User {
name: string;
id: string;
}

const secondLogic = fromPromise(async ({ input }: { input: { id: string }}) => {
const user = await getUser(input.id); // User is inferred

return user;
});

const firstLogic = fromPromise<User, { id: string }>(async ({ input, self, /* ... */ }) => {
const user = await getUser(input.id);

return user;
});

Promise actor output

To get the eventual resolved output of a promise actor, you can subscribe to the promise actor and check the status property of the snapshot. If the status is 'done', you can access the output property of the snapshot to get the resolved value. Otherwise, the output will be undefined.

import { fromPromise } from 'xstate';

const promiseLogic = fromPromise(async () => {
const user = await getUser('123');

return user;
});

const promiseActor = createActor(promiseLogic);

promiseActor.subscribe(snapshot => {
if (snapshot.status === 'done') {
console.log(snapshot.output);
// logs { id: '123', /* other user data */ }
}
});

You can also use toPromise(...) to convert any actor, including promise actors, to a promise. This is useful if you want to await the output of an actor:

import { toPromise, createActor } from 'xstate';
import { somePromiseLogic } from './somePromiseLogic';

const promiseActor = createActor(somePromiseLogic);
promiseActor.start();

const output = await toPromise(promiseActor);

console.log(output);
// logs the resolved output of the promise actor

Promise actor error handling

If an error occurs in the promise logic, the promise actor will reject with the error. You can subscribe to the promise actor and check the status property of the snapshot in an error observer (the error property of the observer object).

import { fromPromise } from 'xstate';

const promiseLogic = fromPromise(async () => {
// ...
throw new Error('Something went wrong');
});

const promiseActor = createActor(promiseLogic);
promiseActor.subscribe({
error: (error) => {
console.error(error);
// logs 'Error: Something went wrong'
}
});

promiseActor.start();