Introducción a la programación asíncrona en PHP
\n\nEl PHP tradicional funciona de forma síncrona: cada operación se ejecuta secuencialmente. Si un script necesita enviar una solicitud HTTP o leer un archivo, espera a que la operación termine, bloqueando la ejecución de todo el código. Para aplicaciones web con alta carga, esto se convierte en un cuello de botella. La programación asíncrona resuelve este problema, permitiendo ejecutar múltiples tareas simultáneamente sin bloquear el hilo principal.
\n\nEn esta guía, analizaremos cómo funciona la asincronía en PHP, qué herramientas están disponibles y cómo usarlas en la práctica. Aprenderá sobre generadores, las bibliotecas ReactPHP y Swoole, así como las nuevas capacidades de PHP 8.1 — Fiber.
\n\nFundamentos de la programación asíncrona
\n\n¿Qué es la asincronía y por qué es necesaria?
\n\nImagina que estás preparando el desayuno. Enfoque síncrono: pones la tetera, esperas a que hierva, luego empiezas a freír los huevos. Enfoque asíncrono: pones la tetera y, mientras el agua se calienta, preparas los huevos revueltos en paralelo. En PHP, esto significa que, en lugar de esperar la respuesta de una base de datos o una API externa, puedes ejecutar otro código.
\n\nPrincipales ventajas del enfoque asíncrono:
\n- \n
- Mayor rendimiento en operaciones de E/S (red, discos, bases de datos) \n
- Ahorro de recursos del servidor: menos hilos procesan más solicitudes \n
- Posibilidad de crear aplicaciones en tiempo real (chats, notificaciones, streaming) \n
El problema del código síncrono
\n\nConsideremos un ejemplo clásico: obtener datos de dos APIs remotas. El código síncrono se ejecutará secuencialmente:
\n\n<?php\nfunction fetchData($url) {\n $ch = curl_init($url);\n curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\n $result = curl_exec($ch); // Bloqueo durante la solicitud\n curl_close($ch);\n return $result;\n}\n\n$start = microtime(true);\n$data1 = fetchData('https://api.example.com/users');\n$data2 = fetchData('https://api.example.com/posts');\necho "Tiempo: " . (microtime(true) - $start);\n?>\n\nSi cada solicitud toma 2 segundos, el tiempo total de ejecución será de 4 segundos, aunque podríamos ejecutar las solicitudes en paralelo.
\n\nHerramientas para la asincronía en PHP
\n\n1. Generadores (yield) — asincronía ligera
\n\nLos generadores aparecieron en PHP 5.5 y permiten crear funciones que pueden pausar su ejecución y devolver el control al código que las llama. Esta es la base para crear corrutinas.
\n\n<?php\nfunction generator() {\n echo "Inicio del generador\\n";\n yield 1;\n echo "Después del primer yield\\n";\n yield 2;\n echo "Fin\\n";\n}\n\n$gen = generator();\nforeach ($gen as $value) {\n echo "Recibido: $value\\n";\n}\n?>\n\nLos generadores se pueden usar para organizar operaciones no bloqueantes junto con un bucle de eventos. Sin embargo, para una asincronía completa, esto no es suficiente: se necesita una biblioteca.
\n\n2. ReactPHP — biblioteca orientada a eventos
\n\nReactPHP es una biblioteca popular para programación asíncrona. Proporciona un bucle de eventos (Event Loop), sockets no bloqueantes, cliente y servidor HTTP. Instalación a través de Composer:
\n\ncomposer require react/event-loop react/http\n\nEjemplo de una solicitud HTTP asíncrona:
\n\n<?php\nrequire 'vendor/autoload.php';\n\nuse React\\EventLoop\\Factory;\nuse React\\HttpClient\\Client;\nuse React\\HttpClient\\Response;\n\n$loop = Factory::create();\n$client = new Client($loop);\n\n$request = $client->request('GET', 'https://api.example.com/data');\n$request->on('response', function (Response $response) {\n $response->on('data', function ($chunk) {\n echo "Fragmento recibido: " . strlen($chunk) . " bytes\\n";\n });\n $response->on('end', function () {\n echo "Solicitud completada\\n";\n });\n});\n$request->end();\n\necho "Solicitud enviada, esperando...\\n";\n$loop->run();\n?