El problema que estás describiendo es doble.
- El programa que está escribiendo debe comportarse de manera asíncrona en su conjunto cuando se ve desde el exterior .
- Debería no ser visibles en el sitio de la llamada si una llamada a la función da potencialmente hasta el control o no.
Hay un par de maneras de lograr esto, pero básicamente se reducen a
- tener múltiples hilos (en algún nivel de abstracción)
- teniendo múltiples tipos de funciones a nivel de lenguaje, todas las cuales se llaman así
foo(4, 7, bar, quux)
.
Para (1), estoy agrupando bifurcando y ejecutando múltiples procesos, generando múltiples hilos de kernel e implementaciones de hilos verdes que programan hilos de nivel de tiempo de ejecución de lenguaje en hilos de kernel. Desde la perspectiva del problema, son lo mismo. En este mundo, ninguna función abandona o pierde el control desde la perspectiva de su hilo . El hilo en sí a veces no tiene control y a veces no se está ejecutando, pero no cedes el control de tu propio hilo en este mundo. Un sistema que se ajuste a este modelo puede o no tener la capacidad de generar nuevos hilos o unirse a los hilos existentes. Un sistema que se ajuste a este modelo puede o no tener la capacidad de duplicar un hilo como el de Unix fork
.
(2) es interesante. Para hacer justicia, necesitamos hablar sobre las formas de introducción y eliminación.
Voy a mostrar por qué lo implícito await
no se puede agregar a un lenguaje como Javascript de una manera compatible con versiones anteriores. La idea básica es que al exponer las promesas al usuario y distinguir entre contextos síncronos y asíncronos, Javascript ha filtrado un detalle de implementación que evita el manejo uniforme de las funciones síncronas y asíncronas. También está el hecho de que no puedes hacer await
una promesa fuera de un cuerpo de función asíncrona. Estas opciones de diseño son incompatibles con "hacer que la asincronía sea invisible para la persona que llama".
Puede introducir una función síncrona utilizando una lambda y eliminarla con una llamada a la función.
Introducción a la función síncrona:
((x) => {return x + x;})
Eliminación de la función sincrónica:
f(4)
((x) => {return x + x;})(4)
Puede contrastar esto con la introducción y eliminación de funciones asincrónicas.
Introducción a la función asincrónica
(async (x) => {return x + x;})
Eliminación de funciones asincrónicas (nota: solo válido dentro de una async
función)
await (async (x) => {return x + x;})(4)
El problema fundamental aquí es que una función asincrónica también es una función síncrona que produce un objeto de promesa .
Aquí hay un ejemplo de llamar a una función asincrónica sincrónicamente en la respuesta node.js
> (async (x) => {return x + x;})(4)
Promise { 8 }
Hipotéticamente puede tener un idioma, incluso uno de tipo dinámico, donde la diferencia entre las llamadas a funciones asíncronas y síncronas no es visible en el sitio de la llamada y posiblemente no sea visible en el sitio de definición.
Es posible tomar un lenguaje como ese y reducirlo a Javascript, solo tendría que hacer que todas las funciones sean asincrónicas de manera efectiva.