Skip to content

Async work and cancellation

Put a request in an invoked state. The service gets a readonly context, the event that entered the state, and an AbortSignal. It is aborted when the actor stops or the machine leaves the invoking state.

ts
const saveFlow = defineMachine<FormContext, Event>()({
  context: () => ({ values: emptyForm(), error: null }),
  initial: 'editing',
  services: {
    save: async ({ context, signal }) => {
      const response = await fetch('/api/orders', {
        method: 'POST', body: JSON.stringify(context.values), signal,
      })
      if (!response.ok) throw new Error('Save failed')
      return response.json()
    },
  },
  actions: {
    captureError: ({ assign, event }) => assign({ error: String((event as { error: Error }).error.message) }),
  },
  states: {
    editing: { on: { SUBMIT: 'saving' } },
    saving: {
      invoke: { src: 'save', onDone: 'complete', onError: { target: 'failure', actions: 'captureError' } },
      on: { CANCEL: 'editing' },
    },
    failure: { on: { RETRY: 'saving', EDIT: 'editing' } },
    complete: {},
  },
})

This eliminates stale responses: a completed request is ignored after its state has been exited or its signal is aborted.