CodeForge

JavaScript de Cero a Experto / Patrones y observables

Máquina de estados: transiciones con permiso

Teoría20 min20 XP

"El botón de pagar se habilitó dos veces", "la petición mostró éxito Y error a la vez", "¿cómo llegó la app a ESTE estado?" — la familia de bugs más desesperante nace de estados implícitos regados en booleanos. La máquina de estados los extermina con una idea de kínder: declarar QUÉ estados existen y QUÉ caminos entre ellos están permitidos. Todo lo demás, prohibido por diseño.

La lavadora

El olor del bug: booleanos en pandilla

el-problema.js
// tres booleanos = 8 combinaciones posibles...
let cargando = false;
let exito = false;
let error = false;

// ...y solo 4 tienen sentido. Las otras 4 son bugs esperando:
// ¿cargando=true Y error=true? ¿exito=true Y error=true?
// cada función debe acordarse de apagar los otros dos. Alguna olvidará.

¿Reconoces el trío? Son los estados de carga del módulo 10 — allá los manejaste con disciplina dentro de un try/catch. La máquina convierte esa disciplina en estructura: UN string de estado y una tabla de caminos.

La máquina en 20 líneas

Lee la tabla como un mapa de la lavadora: cada estado lista SOLO las acciones que acepta y a dónde llevan. "Éxito y error a la vez" ya no es un bug que manejar — es un estado que NO EXISTE: hay un solo string, y solo cambia por caminos declarados. Y el bonus de leerlo: la tabla ES la documentación del flujo — un diseñador la entiende sin saber programar.

Máquina + store: el combo que pinta interfaces

La máquina decide; el store (lección pasada) AVISA. Juntas son el patrón de interfaces robustas: el estado de la máquina vive en un store, la UI se suscribe y pinta según el string:

combo.js
const estadoUI = crearEstado(consulta.estado);

function enviar(accion) {
estadoUI.set(consulta.enviar(accion));   // mover Y avisar, juntos
}

estadoUI.suscribir((estado) => {
// la UI es un mapa estado → pantalla (ni un if de booleanos)
if (estado === "cargando") console.log("UI: spinner");
if (estado === "lista") console.log("UI: mostrar datos");
if (estado === "conError") console.log("UI: mensaje + botón reintentar");
});

Nota honesta de alcance: para flujos de 2 estados, la máquina es burocracia — un booleano honesto basta. Empieza a pagar desde 3-4 estados con reglas de paso (formularios multi-etapa, pedidos, juegos, la autenticación). Y en el mundo real existe XState, la librería que industrializa este patrón (visualizador de diagramas incluido) — cuando la veas, su createMachine te parecerá un déjà vu de tus 20 líneas.

En tu e-commerce, un usuario logró pagar dos veces el mismo pedido haciendo doble clic rapidísimo. Con máquina de estados, ¿por qué es imposible?

Mini-reto

Modela el ciclo de un gasto compartido de "Mi Bolsillo": estados borrador → pendiente → aprobado | rechazado, con reglas: desde borrador solo se puede enviar; desde pendiente, aprobar o rechazar; desde rechazado se puede corregir (vuelve a borrador); aprobado es terminal (ningún camino sale). Pruébala: el camino feliz completo, un rechazo con corrección, y DOS intentos prohibidos atrapados con try/catch. Remate: imprime puede("aprobar") en cada estado — esa función es la que habilitaría botones en la UI.

Qué sigue

Módulo casi cerrado: módulos, singleton, factory, observer, store y máquina — el kit de arquitectura completo. El reto los pone a trabajar JUNTOS: "Mi Bolsillo reactivo", una mini-arquitectura donde la factory fabrica, el bus comunica y las suscripciones mantienen todo sincronizado. Sin DOM, sin framework: puro diseño tuyo.