Async Calculator
Interactive Async Calculator
Section titled “Interactive Async Calculator”About This Example
Section titled “About This Example”This example demonstrates a basic promise machine for handling asynchronous calculations:
- Defines a promise machine with Idle, Pending, Resolved, and Rejected states
- Manages async operation lifecycle automatically
- Provides type-safe event parameters and state payloads
- Shows how promise machines handle success and error cases
The promise machine pattern is perfect for any asynchronous operation - from simple calculations to complex API workflows.
Source Code
Section titled “Source Code”import { createPromiseMachine, withReset } from "matchina";
export function createAsyncCalculatorMachine() { const baseMachine = createPromiseMachine( (a: number, b: number) => new Promise<number>((resolve) => setTimeout(() => resolve(a + b), 1000)) ); return withReset(baseMachine, baseMachine.states.Idle());}
export type AsyncCalculatorMachine = ReturnType< typeof createAsyncCalculatorMachine>;
import { useMachine } from "matchina/react";import { useMemo } from "react";import { createAsyncCalculatorMachine } from "./machine";
export function useAsyncCalculator() { const calculator = useMemo(() => createAsyncCalculatorMachine(), []); useMachine(calculator); return calculator;}
import { useState } from "react";import type { AsyncCalculatorMachine } from "./machine";
interface AsyncCalculatorViewProps { machine: AsyncCalculatorMachine;}
export function AsyncCalculatorView({ machine }: AsyncCalculatorViewProps) { const [a, setA] = useState(5); const [b, setB] = useState(3); const state = machine.getState();
const handleCalculate = () => { machine.execute(a, b); };
return ( <div className="space-y-4 p-4 border rounded-lg"> <div className="space-y-2"> <div className="flex gap-2 items-center"> <input type="number" value={a} onChange={(e) => setA(Number(e.target.value))} className="w-20 px-2 py-1 border rounded" disabled={state.is("Pending")} /> <span>+</span> <input type="number" value={b} onChange={(e) => setB(Number(e.target.value))} className="w-20 px-2 py-1 border rounded" disabled={state.is("Pending")} /> <button onClick={handleCalculate} disabled={state.is("Pending")} className="px-4 py-1 bg-blue-500 text-white rounded disabled:opacity-50" > Calculate </button> {!state.is("Idle") && ( <button onClick={machine.reset} className="px-4 py-1 bg-gray-500 text-white rounded" > Reset </button> )} </div> </div>
<div className="p-3 bg-gray-50 rounded"> {state.match({ Idle: () => <span>Ready to calculate</span>, Pending: ({ params }: any) => ( <span> Calculating {params[0]} + {params[1]}... </span> ), Resolved: (result: any) => ( <span className="text-green-600">Result: {result}</span> ), Rejected: (error: any) => ( <span className="text-red-600">Error: {error.message}</span> ), })} </div> </div> );}
import { useState } from "react";import { AsyncCalculatorView } from "./AsyncCalculatorView";import { createAsyncCalculatorMachine } from "./machine";
export function AsyncCalculator() { const [calculator] = useState(() => createAsyncCalculatorMachine()); return <AsyncCalculatorView machine={calculator} />;}