Estoy haciendo algunas pruebas unitarias. El marco de prueba carga una página en un iFrame y luego ejecuta afirmaciones en esa página. Antes de que comience cada prueba, creo un Promise
que establece el onload
evento del iFrame para llamar resolve()
, establece el iFrame src
y devuelve la promesa.
Entonces, puedo simplemente llamar loadUrl(url).then(myFunc)
, y esperará a que se cargue la página antes de ejecutar lo que myFunc
sea.
Utilizo este tipo de patrón en todas partes en mis pruebas (no solo para cargar URLs), principalmente para permitir que ocurran cambios en el DOM (por ejemplo, imitar al hacer clic en un botón y esperar a que los divs se oculten y muestren).
La desventaja de este diseño es que constantemente escribo funciones anónimas con algunas líneas de código en ellas. Además, aunque tengo una solución alternativa (QUnit assert.async()
), la función de prueba que define las promesas se completa antes de que se ejecute la promesa.
Me pregunto si hay alguna forma de obtener un valor de a Promise
o esperar (bloquear / dormir) hasta que se resuelva, similar a .NET IAsyncResult.WaitHandle.WaitOne()
. Sé que JavaScript es de un solo subproceso, pero espero que eso no signifique que una función no pueda ceder.
En esencia, ¿hay alguna manera de obtener lo siguiente para escupir los resultados en el orden correcto?
function kickOff() {
return new Promise(function(resolve, reject) {
$("#output").append("start");
setTimeout(function() {
resolve();
}, 1000);
}).then(function() {
$("#output").append(" middle");
return " end";
});
};
function getResultFrom(promise) {
// todo
return " end";
}
var promise = kickOff();
var result = getResultFrom(promise);
$("#output").append(result);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="output"></div>
fuente
.then(fnAppend.bind(myDiv))
, que pueden reducir enormemente los anones.Respuestas:
La generación actual de Javascript en los navegadores no tiene un
wait()
osleep()
que permita que se ejecuten otras cosas. Entonces, simplemente no puede hacer lo que está pidiendo. En cambio, tiene operaciones asincrónicas que harán lo suyo y luego lo llamarán cuando hayan terminado (como ha estado usando promesas).Parte de esto se debe al único subproceso de Javascript. Si el hilo único está girando, entonces ningún otro Javascript se puede ejecutar hasta que ese hilo giratorio esté terminado. ES6 presenta
yield
y generadores que permitirán algunos trucos cooperativos como ese, pero estamos muy lejos de poder usarlos en una amplia muestra de navegadores instalados (se pueden usar en algunos desarrollos del lado del servidor donde controlas el motor JS que se está utilizando).La administración cuidadosa del código basado en promesas puede controlar el orden de ejecución de muchas operaciones asíncronas.
No estoy seguro de entender exactamente qué orden está tratando de lograr en su código, pero podría hacer algo como esto usando su
kickOff()
función existente y luego adjuntarle un.then()
controlador después de llamarlo:Esto devolverá la salida en un orden garantizado, como este:
Actualización en 2018 (tres años después de que se escribió esta respuesta):
Si transpila su código o ejecuta su código en un entorno que admita características de ES7 como
async
yawait
, ahora puede usarloawait
para hacer que su código "parezca" esperar el resultado de una promesa. Todavía se está desarrollando con promesas. Aún no bloquea todo Javascript, pero le permite escribir operaciones secuenciales en una sintaxis más amigable.En lugar de la forma de hacer las cosas de ES6:
Puedes hacerlo:
fuente
then()
es lo que he estado haciendo. Simplemente no me gusta tener que escribirfunction() { ... }
todo el tiempo. Desordena el código.(foo) => { ... }
en lugar defunction(foo) { ... }
foo => single.expression(here)
para deshacerse de los paréntesis rizados y redondos.async
y laawait
sintaxis como una opción que ahora está disponible en node.js y en los navegadores modernos o mediante transpilers.Si se utiliza ES2016 puede utilizar
async
yawait
y hacer algo como:Si usa ES2015, puede usar generadores . Si no le gusta la sintaxis, puede abstraerla usando una
async
función de utilidad como se explica aquí .Si usa ES5 probablemente querrá una biblioteca como Bluebird para darle más control.
Finalmente, si su tiempo de ejecución es compatible con ES2015, el orden de ejecución puede conservarse con paralelismo usando Fetch Injection .
fuente
async
/await
.async
/await
es solo otra sintaxis dePromise.then
.async
, no esperará ... a menos que ceda el usoawait
. El ejemplo de ES2016 / ES7 todavía no se puede ejecutar, así que aquí hay un código que escribí hace tres años para demostrar el comportamiento.Otra opción es usar Promise.all para esperar a que se resuelva una serie de promesas. El código a continuación muestra cómo esperar a que se resuelvan todas las promesas y luego lidiar con los resultados una vez que estén todos listos (ya que ese parecía ser el objetivo de la pregunta); Sin embargo, el inicio podría obviamente salir antes de que el medio se haya resuelto simplemente agregando ese código antes de que llame a resolverse.
fuente
Puedes hacerlo manualmente. (Lo sé, esa no es una gran solución, pero ...) use el
while
bucle hasta queresult
no tenga un valorfuente