Parte 7 — Paradigmas · ⏱️ Duración estimada: 90 min · Nivel: Intermedio ✅ Clase construida — 10 implementaciones del núcleo verificadas contra
casos.json.
La clase anterior repartía trabajo entre hilos que corrían de verdad a la vez. El paradigma asíncrono persigue un objetivo parecido —no quedarse parado esperando— pero con una filosofía opuesta: un solo hilo que, en vez de bloquearse mientras aguarda una operación lenta, la inicia, la deja en marcha y sigue haciendo otra cosa, para retomarla cuando el resultado esté listo. No hay paralelismo; hay concurrencia sin paralelismo. La palabra clave es no bloquear: cuando pides un dato a la red o al disco, no congelas el programa entero durante los milisegundos (o segundos) que tarda la respuesta.
La pieza que representa "un valor que todavía no está pero llegará" es la promesa (o future, o task). Es un objeto que devuelves de inmediato como un pagaré: "aquí tienes un recibo; cuando el cálculo termine, contendrá el valor". El operador await es lo que canjeas ese recibo: suspende tu función en ese punto, devuelve el control al bucle de eventos —el mismo motor que viste en la clase 119— y reanuda tu función justo donde la dejaste cuando la promesa se resuelve. Bajo el azúcar, async/await es una máquina de estados: el compilador trocea tu función en los puntos de await y guarda dónde iba para poder continuar después. Es, en el fondo, azúcar sintáctico sobre continuaciones y callbacks: lo que en la clase 119 escribías como pirámide de callbacks anidados, aquí se lee como código secuencial de arriba abajo.
Nuestro laboratorio reduce el paradigma a su gesto mínimo —una tarea asíncrona que calcula el doble de un número y se espera su valor— para que veas la forma async/await sin el ruido de la I/O real. El cálculo es instantáneo, pero la estructura (definir una operación asíncrona, iniciarla, esperar su resultado con await) es idéntica a la de leer un archivo o consultar una base de datos, que es donde este estilo despliega toda su ventaja.
Al finalizar, podrás:
| # | Tema | Por qué importa |
|---|---|---|
| 1 | Asíncrono | No bloquear mientras se espera |
| 2 | async/await | Esperar sin bloquear el hilo |
| 3 | Tarea/promesa/future | El resultado que llegará |
Leer de la red, de disco o de una base de datos tarda. En vez de bloquear el hilo, async/await inicia la operación y continúa, esperando el resultado cuando llega. Clave en servidores y UI.
nresultado=<2n>Especificación y verificación en casos.json:
| stdin | esperado |
|---|---|
5 |
resultado=10 |
0 |
resultado=0 |
6 |
resultado=12 |
async doble(x): DEVOLVER 2x ; resultado <- await doble(n)
Mismo algoritmo, forma idiomática en cada lenguaje. Todas producen la salida de casos.json.
Cada bloque es el archivo real de implementaciones/: el enlace de cada lenguaje abre su fuente, y el comando de al lado lo ejecuta.
python/main.py · python main.pyimport sys
import asyncio
async def doble(x):
return x * 2
async def main():
n = int(sys.stdin.readline())
resultado = await doble(n)
print(f"resultado={resultado}")
asyncio.run(main())
🧬 El mismo programa en la familia Scripting dinámico: Ruby · Perl · Lua · Tcl · R
javascript/main.mjs · node main.mjsimport { readFileSync } from "node:fs";
async function doble(x) {
return x * 2;
}
const n = parseInt(readFileSync(0, "utf8").trim(), 10);
(async () => {
const resultado = await doble(n);
console.log(`resultado=${resultado}`);
})();
🧬 El mismo programa en la familia JavaScript / web: Dart · ActionScript
typescript/main.ts · pnpm exec tsx main.tsimport { readFileSync } from "node:fs";
async function doble(x: number): Promise<number> {
return x * 2;
}
const n: number = parseInt(readFileSync(0, "utf8").trim(), 10);
(async () => {
const resultado: number = await doble(n);
console.log(`resultado=${resultado}`);
})();
🧬 El mismo programa en la familia JavaScript / web: Dart · ActionScript
java/Main.java · java Main.javaimport java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
public class Main {
public static void main(String[] args) throws IOException, InterruptedException, ExecutionException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
int n = Integer.parseInt(br.readLine().trim());
CompletableFuture<Integer> tarea = CompletableFuture.supplyAsync(() -> n * 2);
System.out.println("resultado=" + tarea.get());
}
}
🧬 El mismo programa en la familia JVM: Kotlin · Scala · Groovy · Clojure
csharp/Program.cs · dotnet runusing System;
using System.Threading.Tasks;
async Task<int> Doble(int x) => await Task.FromResult(x * 2);
int n = int.Parse(Console.In.ReadToEnd().Trim());
int resultado = await Doble(n);
Console.WriteLine($"resultado={resultado}");
🧬 El mismo programa en la familia .NET: F# · VB.NET
go/main.go · go run main.gopackage main
import (
"bufio"
"fmt"
"os"
"strconv"
"strings"
)
func main() {
line, _ := bufio.NewReader(os.Stdin).ReadString('\n')
n, _ := strconv.Atoi(strings.TrimSpace(line))
// Go usa goroutines y canales en lugar de async/await.
ch := make(chan int, 1)
go func() { ch <- n * 2 }()
fmt.Printf("resultado=%d\n", <-ch)
}
🧬 El mismo programa en la familia Sistemas: Zig · Nim · D
rust/main.rs · rustc main.rs -o main && ./mainuse std::io::Read;
fn main() {
let mut s = String::new();
std::io::stdin().read_to_string(&mut s).unwrap();
let n: i64 = s.trim().parse().unwrap();
// Sin runtime async externo, se muestra el resultado de la tarea.
let resultado = n * 2;
println!("resultado={resultado}");
}
🧬 El mismo programa en la familia Sistemas: Zig · Nim · D
c/main.c · cc main.c -o main && ./main#include <stdio.h>
int main(void) {
long n;
if (scanf("%ld", &n) != 1) return 1;
/* C no tiene async a nivel de lenguaje; se calcula el resultado. */
printf("resultado=%ld\n", n * 2);
return 0;
}
🧬 El mismo programa en la familia C / llaves: C++ · Objective-C
sql/main.sql · sqlite3 :memory: < main.sql-- SQL no tiene async a nivel de lenguaje.
WITH nums(n) AS (VALUES (5), (0), (6))
SELECT printf('resultado=%d', n * 2) AS resultado FROM nums;
🧬 El mismo programa en la familia Lógica y declarativa: Prolog · Datalog
php/main.php · php main.php<?php
$n = (int) trim(fgets(STDIN));
// PHP es síncrono por defecto; se muestra el resultado de la tarea.
echo "resultado=" . ($n * 2) . "\n";
🧬 El mismo programa en la familia Scripting dinámico: Ruby · Perl · Lua · Tcl · R
SQL es declarativo: no lee de stdin como los demás; su implementación muestra la misma idea sobre una tabla de casos, y el verificador la marca como ilustrativa.
| Clase de diferencia | Observación entre lenguajes |
|---|---|
| Sintáctica | async/await (JS/TS/Python/C#/Rust), goroutines+canales (Go). |
| Semántica | await no bloquea el hilo; libera para otras tareas. |
| Paradigmática | SQL no tiene async a nivel de lenguaje. |
JavaScript popularizó async/await; hoy está en Python, C#, Rust y otros. Go usa goroutines en su lugar.
Los mismos casos para todas las implementaciones: casos.json. Verifica la equivalencia:
python scripts/verificar_equivalencia.py 122
Detalle en reto.md.
Libros de la parte:
Libros de los lenguajes del núcleo:
⏮️ Clase 121 · 📂 Parte · 📚 Índice · 🌐 Atlas · Clase 123 ⏭️