Estoy tratando de hacer (otro idioma) que se compila a JavaScript. Una de las características que me gustaría tener es la capacidad de realizar las operaciones asíncronas de JavaScript sincrónicamente (no exactamente sincrónicamente, sin bloquear el hilo principal, por supuesto). Menos charla, 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 volver a compilarlo en JS?
Los criterios, ordenados por importancia:
- Actuación
- Compatibilidad al revés
- Legibilidad compilada del código (no es realmente importante)
Hay varias formas posibles de implementarlo, esos tres me vinieron a la mente:
Usando promesas:
f(); a( b() );
se convertiría enf().then(function(){ return b() }).then(function(x){ a(x) });
- Pros: relativamente simple de implementar, polifilizable de vuelta a ES3
- contras: crear una nueva función y una instancia de Proxy para cada llamada de función
-
f(); a( b() );
se convertiría enyield f(); tmp = yield b(); yield a( tmp );
- pros: javascript más agradable
- contras: crear proxies, generadores e iteradores en cada paso, tampoco polifilables
- EDITAR: de hecho, son polifilables usando otro recompilador (por ejemplo, Regenerator )
Usando XMLHttpRequest sincrónico:
- solicite un script de servidor que espere hasta otra llamada de otro lugar en el código
- pros: de facto no hay cambios en el código, súper fácil de implementar
- contras: requiere script del servidor (=> no portabilidad, solo navegador); Además, XMLHttpRequest síncrono bloquea el hilo para que no funcione en absoluto
- EDITAR: en realidad funcionaría dentro de un trabajador
El principal problema es que no se 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 el JS puro. Entonces pregunto:
¿Hay mejores formas de implementar?
De las respuestas:
Aguardar palabra clave (@ MI3Guy)
- C # way (que es bueno !)
- introduce una nueva palabra clave (que se puede disfrazar como una función (ciudadano de primera clase) que, por ejemplo, arroja si el programador intenta pasarla como argumento)
- ¿Cómo saber que la función es asíncrona? No hay otras palabras clave!
- ¿Cada función que contiene la
await
palabra clave se vuelve asíncrona? - Cuando llega el código
await
, ¿devuelve una promesa? ¿Otra cosa se trata como una función ordinaria?
- ¿Cada función que contiene la
synchronously (without blocking the thread, of course)
Usted sigue usando esa palabra. No creo que signifique lo que crees que significa, porque sincrónicamente significa "bloquear el hilo".f(); a( b() );
se convertiría enf().then(b).then(a);
No incluye funciones.Respuestas:
Suponiendo que el idioma se escribe dinámicamente, puedo ver dos formas diferentes de manejar esto sin saber en tiempo de 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 mucho código extra. Esto es esencialmente lo que está haciendo en la opción 2.
Otra opción es usar fibras. Las fibras son como hilos livianos programados por el programa. La fibra misma 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 dentro de node.js. Mi conjetura es que si está compilando para JS que el objetivo (o al menos un objetivo) es ejecutarse en el navegador 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 de inmediato. Esto permite que se realicen múltiples operaciones asincrónicas 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 cuando algunos datos que está utilizando son modificados por un código externo.En C # dentro de un método marcado como asíncrono, la instrucción return se usa para devolver el valor que estará dentro del futuro, no en el futuro mismo. Sin embargo, tenga en cuenta que el modificador asíncrono 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 usted dijo) 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 alcanzarlo, la función aún debería devolver un futuro. También recomendaría exponer alguna forma de convertir un valor en un futuro completo que contenga el valor, especialmente si no hay forma de hacerlo implícitamente agregando el modificador asíncrono.fuente
if(){} else{}
, el programador también podría definir una estructura similarifZero(){} elseWhile(){}
). Pero parece que tengo que introducir una función de espera global que arroja (o al menos devuelve indefinido) si el programador intenta asignar su valor a una variable (las funciones son ciudadanos de primera clase).