Volver a explorar Snippets

Node.js CLI Calculator: Arquitectura Funcional y Asíncrona

JAVASCRIPT 11 de mayo de 2026 45 lecturas
Calculadora interactiva para terminal desarrollada en Node.js. Implementa validación de errores, manejo de promesas con Readline y una estructura de diccionario para operaciones aritméticas.

Arquitectura Limpia en Aplicaciones de Consola (CLI) con Node.js

Cuando aprendemos a programar, la calculadora suele ser uno de los primeros proyectos que construimos. Sin embargo, en un entorno profesional de backend con Node.js, construir una Interfaz de Línea de Comandos (CLI) interactiva requiere manejar flujos de datos asíncronos y estructuras de control eficientes.

Este snippet no es solo una calculadora; es una demostración de Arquitectura Funcional y Programación Asíncrona aplicada a la terminal del sistema, reemplazando los antipatrones comunes por soluciones robustas y escalables.

1. El Patrón Diccionario (Adiós al Switch/Case)

El error más común al evaluar múltiples operadores aritméticos es crear un bloque switch gigante o una cadena interminable de sentencias if-else. En este código, implementamos un Diccionario de Operaciones (Hash Map) utilizando un objeto literal de JavaScript.

Cada clave del objeto es un operador (ej. +), y su valor es una función de flecha anónima que ejecuta la lógica correspondiente. Esto proporciona un tiempo de acceso de O(1) y hace que el código sea infinitamente escalable: si el día de mañana queremos agregar una operación de raíz cuadrada o potencia, solo agregamos una línea al diccionario sin alterar la lógica de ejecución principal.

2. "Promesificación" del módulo Readline

El módulo nativo readline de Node.js está diseñado históricamente con una arquitectura basada en callbacks. Preguntar múltiples cosas al usuario de forma secuencial generaría el temido Callback Hell (Código en forma de pirámide).

Para solucionar esto y mantener un código limpio, creé una función de utilidad llamada question que envuelve el método rl.question nativo dentro de una Promesa. Esto nos permite utilizar la sintaxis moderna de async/await en nuestra función principal runCalculator(), haciendo que el código asíncrono se lea de forma secuencial y estructurada, exactamente igual que el código síncrono.

3. Manejo de Excepciones y Robustez (Try/Catch)

Un producto de software profesional no asume que el usuario ingresará los datos correctos. El bloque try/catch/finally es vital aquí:

  • Validación de Tipos: Forzamos la conversión a flotantes (parseFloat) y verificamos inmediatamente con isNaN() si el usuario introdujo letras en lugar de números, lanzando un error personalizado antes de intentar cualquier cálculo.
  • Manejo de Casos Límite: El diccionario contiene un operador ternario explícito para evitar el colapso del sistema matemático por una división por cero.
  • Liberación de Memoria: El bloque finally garantiza que, sin importar si la ejecución fue un éxito o arrojó un error, el hilo de readline se cierre correctamente con rl.close(), evitando fugas de memoria (memory leaks) en el proceso de Node.js.

Pruébalo en la Terminal

Al ejecutar este código en un entorno interactivo (como el Playground adjunto basado en Worker Threads), notarás cómo el Event Loop de Node.js se pausa educadamente, esperando tu interacción gracias a la implementación de Promesas. Es la base perfecta para construir CLI tools más complejas como generadores de código o asistentes de configuración de servidores.

/**
 * 🧮 CLI Calculator - Node.js
 * Una implementación profesional de calculadora para consola.
 */

const readline = require('readline');

// Interfaz para entrada y salida de datos
const rl = readline.createInterface({
    input: process.stdin,
    output: process.stdout
});

// Diccionario de operaciones para evitar condicionales extensos
const operations = {
    '+': (a, b) => a + b,
    '-': (a, b) => a - b,
    '*': (a, b) => a * b,
    '/': (a, b) => b !== 0 ? a / b : 'Error: División por cero'
};

// Función de utilidad para manejar preguntas como Promesas
const question = (query) => new Promise((resolve) => rl.question(query, resolve));

async function runCalculator() {
    console.clear();
    console.log('--- ⚡ ANDRÉSSY | CLI CALCULATOR ---');

    try {
        const num1 = parseFloat(await question('🔢 Ingresa el primer número: '));
        const operator = await question('➕ Selecciona operación (+, -, *, /): ');
        const num2 = parseFloat(await question('🔢 Ingresa el segundo número: '));

        if (isNaN(num1) || isNaN(num2)) {
            throw new Error('Entrada inválida: Los valores deben ser números.');
        }

        const execute = operations[operator];
        
        if (execute) {
            const result = execute(num1, num2);
            console.log(`\n✅ Resultado: ${num1} ${operator} ${num2} = ${result}`);
        } else {
            console.log('\n❌ Operación no reconocida.');
        }

    } catch (error) {
        console.error(`\n⚠️  ${error.message}`);
    } finally {
        console.log('\n------------------------------------');
        rl.close();
    }
}

// Ejecución del Script
runCalculator();
¿Qué te pareció?
🔥 Brillante 0
💡 Me sirvió 0
🚀 A otro nivel 0