Idioma compilado para JS: la forma más elegante de hacer esperas de estilo sincrónico

8

Estoy tratando de hacer (otro) lenguaje que compile a JavaScript. Una de las características que me gustaría tener es la capacidad de realizar operaciones asíncronas de JavaScript sincrónicamente (no exactamente de forma sincrónica, sin bloquear el hilo principal, por supuesto). Menos palabras, más ejemplos:

/* These two snippets should do exactly the same thing */

// the js way
var url = 'file.txt';
fetch(url)
    .then(function (data) {
        data = "(" + data + ")";
        return sendToServer(data);
    }).then(function (response) {
        console.log(response);
    });

// synchronous-like way
var url = 'file.txt';
var response = sendToServer("(" + fetch(url) + ")");
console.log(response);

¿Cuál es la forma más elegante de compilar de nuevo a JS?

Los criterios, ordenados por importancia:

  1. Rendimiento
  2. Compatibilidad con versiones anteriores
  3. La legibilidad del código compilado (no es realmente importante)

Hay varias formas posibles de implementarlo, esos tres vinieron a mi mente:

  1. Usando promesas:

    • f(); a( b() ); se convertiría en
    • f().then(function(){ return b() }).then(function(x){ a(x) });
    • ventajas: relativamente sencillas de implementar, rellenadas con polietileno de regreso a ES3
    • contras: creando una nueva función y una instancia de Proxy para cada llamada de función
  2. Uso de generadores :

    • f(); a( b() ); se convertiría en
    • yield f(); tmp = yield b(); yield a( tmp );
    • pros: mejor javascript
    • contras: la creación de proxies, generadores e iteradores en cada paso, tampoco es poli-rellenable
    • EDITAR: de hecho, se pueden rellenar con otro compilador (p. ej., Regenerator )
  3. Utilizando XMLHttpRequest síncrono:

    • solicite un script de servidor que espere hasta otra llamada desde otro lugar en el código
    • pros: de facto sin cambios en el código, súper fácil de implementar
    • contras: requiere una secuencia de comandos del servidor (= > sin portabilidad, solo para el navegador); además, XMLHttpRequest sincroniza el subproceso para que no funcione en absoluto
    • EDITAR: en realidad funcionaría dentro de un Worker

El problema principal es que uno no puede saber si una función es asíncrona hasta el tiempo de ejecución. Eso da como resultado que las dos soluciones que al menos funcionan sean mucho más lentas que las JS puras. Así que pregunto:

¿Hay mejores formas de implementar?

De las respuestas:

Esperar palabra clave (@ MI3Guy)

  • C # way (que es bueno !)
  • introduce una nueva palabra clave (que puede disfrazarse como una función (ciudadano de primera clase) que, por ejemplo, lanza si el programador intenta pasarla como un argumento)
  • ¿Cómo saber que la función es asíncrona? No hay otras palabras clave!
    • ¿Todas las funciones que contienen la palabra clave await se vuelven asíncronas?
    • Cuando el código llega a await , devuelve una promesa? ¿El otro trata como una función ordinaria?
pregunta m93a 08.04.2015 - 18:51

1 respuesta

3

Suponiendo que el lenguaje se escribe dinámicamente, puedo ver dos formas diferentes de manejar esto sin saber en el momento de la compilación si una llamada es asíncrona.

Un enfoque sería asumir que cada llamada podría ser asíncrona. Sin embargo, esto sería increíblemente ineficiente y produciría una gran cantidad de código adicional. Esto es esencialmente lo que estás haciendo en la opción 2.

Otra opción es usar fibras. Las fibras son como hilos livianos que están programados por el programa. La propia fibra decide cuándo renunciar al control. Sin embargo, estos requieren soporte del sistema operativo. Por lo que puedo decir, la única forma de usar fibras en JavaScript es en node.js. Mi conjetura es que si está compilando para JS, el objetivo (o al menos un objetivo) es ejecutar en el navegador lo que descarta esta opción. Esta sería una versión más ligera de la opción 3.

Honestamente, consideraría agregar una palabra clave como C # 's await . Esto tiene una serie de ventajas además de especificar que una función es asíncrona. Se pueden llamar funciones para producir futuros sin esperarlos inmediatamente. Esto permite que se realicen múltiples operaciones asíncronas a la vez en lugar de en secuencia. Además, es mejor ser explícito sobre qué operaciones son asíncronas porque se puede ejecutar otro código mientras se lleva a cabo la operación. Si async es automático, puede ser sorprendente que algunos datos que esté utilizando se modifiquen con un código externo.

En C # dentro de un método marcado como asíncrono, la declaración de retorno se usa para devolver el valor que estará dentro del futuro, no el futuro en sí. Sin embargo, tenga en cuenta que el modificador async no es realmente parte de la firma del método. await se puede usar con cualquier método que devuelva un futuro.

Si no desea agregar un modificador asíncrono para las funciones, puede decidir (como lo indicó) que cualquier función con una espera se trata como si tuviera un modificador asíncrono implícito. Entonces, incluso si una función tiene un await , pero regresa antes de alcanzarla, la función debería devolver un futuro. También recomendaría exponer alguna forma de convertir un valor en un futuro completado que contenga el valor, especialmente si no hay forma de hacerlo implícitamente agregando el modificador asíncrono.

    
respondido por el MI3Guy 09.04.2015 - 01:20

Lea otras preguntas en las etiquetas