Este artículo explora la evolución de las técnicas para manejar la concurrencia en sistemas informáticos, desde el problema original de C10K (manejar miles de conexiones simultáneas) hasta la adopción generalizada de async/await. Inicialmente, la solución fue el uso de hilos del sistema operativo, pero su alto costo en recursos (memoria y tiempo de cambio de contexto) hacía inviable esta opción para aplicaciones de alta concurrencia.
La primera ola de soluciones se basó en callbacks. Estos permitían evitar el bloqueo de hilos, registrando funciones para ser ejecutadas cuando una operación de I/O finalizaba. Aunque resolvió el problema de recursos, los callbacks llevaron al infame “callback hell”, código difícil de leer, mantener y con manejo de errores fragmentado, además de la imposibilidad de cancelar operaciones asíncronas.
La siguiente generación introdujo Promises (o Futures), que representaban el resultado eventual de una operación asíncrona como un objeto. Esto mejoró la legibilidad y el manejo de errores al permitir la composición de operaciones y la captura centralizada de errores. Sin embargo, los Promises son de uso único, lo que los hace inadecuados para manejar flujos continuos de datos como WebSockets, y su composición puede volverse engorrosa para escenarios complejos. Además, los errores no manejados podían desaparecer silenciosamente, creando problemas de depuración.
Finalmente, async/await surgió como una solución que permitía escribir código asíncrono que se parecía a código síncrono. Esta sintaxis simplifica la lectura y el manejo de errores, integrando bien las operaciones asíncronas en el flujo de control del programa. Aunque async/await ha sido ampliamente adoptado, introduce el problema de “coloring functions” (funciones que pueden retornar un valor o una promesa), lo que complica el diseño de APIs y la interoperabilidad entre funciones síncronas y asíncronas. El artículo concluye que cada solución ha resuelto los problemas de la anterior, pero ha introducido nuevos desafíos, impulsando la continua evolución de las técnicas de programación concurrente.
