ilay zeidman
Tengo una función javascript como esta:
function myFunction(number) {
var x=number;
...
... more initializations
//here need to wait until flag==true
while(flag==false)
{}
...
... do something
}
El problema es que el javascript se atascó en el tiempo y atascó mi programa. entonces mi pregunta es ¿cómo puedo esperar en medio de la función hasta que el indicador sea verdadero sin “esperar ocupado”?
Kiran
Javascript tiene un solo subproceso, de ahí el comportamiento de bloqueo de página. Puede utilizar el enfoque diferido/promesa sugerido por otros. La forma más básica sería usar window.setTimeout
. P.ej
function checkFlag() {
if(flag === false) {
window.setTimeout(checkFlag, 100); /* this checks the flag every 100 milliseconds*/
} else {
/* do something*/
}
}
checkFlag();
Aquí hay un buen tutorial con más explicaciones: Tutorial
EDITAR
Como señalaron otros, la mejor manera sería reestructurar su código para usar devoluciones de llamada. Sin embargo, esta respuesta debería darle una idea de cómo puede ‘simular’ un comportamiento asíncrono con window.setTimeout
.
-
Si bien, por un lado, me gusta mucho esta respuesta porque, de hecho, es una ‘espera’ de js, no se vuelve tan útil si desea devolver un valor. Si no devuelve un valor, no estoy tan seguro de que haya un caso de uso real para el patrón.
– Martín Meeser
18/04/2016 a las 19:54
-
Por supuesto, puede devolver una promesa e implementar la función de esa manera. Sin embargo, esto generalmente requeriría una biblioteca de terceros que implemente promesas o polyfill, a menos que esté utilizando ECMA-262. Sin devolver una promesa, la mejor manera es utilizar un mecanismo de devolución de llamada para señalar a la persona que llama que hay un resultado disponible.
– Kiran
4 de noviembre de 2016 a las 4:21
-
También puede pasar parámetros si es necesario: stackoverflow.com/questions/1190642/…
– SharpC
20 sep 2017 a las 8:00
-
Esta es una gran respuesta. Casi me rendía después de buscar en muchos foros de tecnología. Creo que funcionó perfectamente para mí porque estaba devolviendo un valor. También funcionó en Internet Explorer. Muchas gracias.
– Joseph
2 oct 2019 a las 20:02
-
Si por alguna razón necesitamos enviar parámetros a la función checkFlag, entonces tenemos que usar la función anónima/flecha como,
window.setTimeout( () => { checkFlag(params); }, 100);
– Said
16 de febrero de 2021 a las 14:00
amigo00
Debido a que javascript en un navegador tiene un solo subproceso (excepto para los trabajadores web que no están involucrados aquí) y un subproceso de ejecución de javascript se ejecuta hasta completarse antes de que se pueda ejecutar otro, su declaración:
while(flag==false) {}
simplemente se ejecutará para siempre (o hasta que el navegador se queje de un bucle de javascript que no responde), la página parecerá estar bloqueada y ningún otro javascript tendrá la oportunidad de ejecutarse, por lo que el valor de la bandera nunca se puede cambiar.
Para un poco más de explicación, Javascript es un lenguaje dirigido por eventos. Eso significa que ejecuta una parte de Javascript hasta que devuelve el control al intérprete. Luego, solo cuando regresa al intérprete, Javascript obtiene el siguiente evento de la cola de eventos y lo ejecuta.
Todas las cosas, como temporizadores y eventos de red, se ejecutan en la cola de eventos. Entonces, cuando se dispara un temporizador o llega una solicitud de red, nunca “interrumpe” el Javascript que se está ejecutando actualmente. En cambio, un evento se coloca en la cola de eventos de Javascript y luego, cuando finaliza el Javascript que se está ejecutando actualmente, el siguiente evento se extrae de la cola de eventos y le toca ejecutarse.
Entonces, cuando haces un bucle infinito como while(flag==false) {}
el Javascript que se ejecuta actualmente nunca finaliza y, por lo tanto, el siguiente evento nunca se extrae de la cola de eventos y, por lo tanto, el valor de flag
nunca se cambia. La clave aquí es que Javascript no está controlado por interrupciones. Cuando se dispara un temporizador, no interrumpe el Javascript que se está ejecutando actualmente, ejecuta otro Javascript y luego deja que el Javascript que se está ejecutando actualmente continúe. Simplemente se coloca en la cola de eventos esperando hasta que el Javascript que se está ejecutando actualmente termine para obtener su turno para ejecutarse.
Lo que debe hacer es repensar cómo funciona su código y encontrar una forma diferente de activar cualquier código que desee ejecutar cuando el flag
cambios de valor. Javascript está diseñado como un lenguaje basado en eventos. Entonces, lo que debe hacer es averiguar en qué eventos puede registrar un interés para que pueda escuchar el evento que podría causar que la bandera cambie y pueda examinar la bandera en ese evento o puede desencadenar su propio evento desde cualquier código podría cambiar la bandera o puede implementar una función de devolución de llamada que cualquier código que cambie esa bandera puede llamar a su devolución de llamada cada vez que la pieza de código responsable de cambiar el valor de la bandera cambiaría su valor a true
solo llama a la función de devolución de llamada y, por lo tanto, a su código que desea ejecutar cuando la bandera se establece en true
llegará a funcionar en el momento adecuado. Esto es mucho, mucho más eficiente que tratar de usar algún tipo de temporizador para verificar constantemente el valor de la bandera.
function codeThatMightChangeFlag(callback) {
// do a bunch of stuff
if (condition happens to change flag value) {
// call the callback to notify other code
callback();
}
}
Yaroslav Dobzhanskij
Solución usando Promesaasíncrono\esperar y EventEmitter lo que permite reaccionar inmediatamente al cambio de bandera sin ningún tipo de bucles
const EventEmitter = require('events');
const bus = new EventEmitter();
let lock = false;
async function lockable() {
if (lock) await new Promise(resolve => bus.once('unlocked', resolve));
....
lock = true;
...some logic....
lock = false;
bus.emit('unlocked');
}
EventEmitter
está incorporado en el nodo. En el navegador, deberá incluirlo usted mismo, por ejemplo, utilizando este paquete: https://www.npmjs.com/package/eventemitter3
-
Un ejemplo de cómo se puede usar este código: (1) Al principio,
lock
Es falso. (2) Algunas llamadas de códigolockable
. Ese código evalúaif (lock)
a falso, por lo que continúa: establecelock
a verdadero, luego pasa a ejecutar algo de lógica. Mientras tanto: (3) Algunas otras llamadas de códigolockable
. Ese código, sin embargo, evalúaif (lock)
a verdadero, por lo que espera en la promesa, hasta que ununlocked
se emitirá el evento. (4) Vuelta al primer código de llamada: Finaliza su lógica, establecelock
a falso, y emite ununlocked
evento. (5) El otro código ahora puede continuar su ejecución.– OfirD
8 de junio de 2021 a las 18:23
ES6 con Async / Await,
let meaningOfLife = false;
async function waitForMeaningOfLife(){
while (true){
if (meaningOfLife) { console.log(42); return };
await null; // prevents app from hanging
}
}
waitForMeaningOfLife();
setTimeout(()=>meaningOfLife=true,420)
Solución moderna usando Promise
myFunction()
en la pregunta original se puede modificar de la siguiente manera
async function myFunction(number) {
var x=number;
...
... more initializations
await until(_ => flag == true);
...
... do something
}
dónde until()
es esta función de utilidad
function until(conditionFunction) {
const poll = resolve => {
if(conditionFunction()) resolve();
else setTimeout(_ => poll(resolve), 400);
}
return new Promise(poll);
}
Algunas referencias a las funciones async/await y arrow se encuentran en una publicación similar: https://stackoverflow.com/a/52652681/209794
-
Esta es la única solución que he visto que le permite “esperar” fácilmente una señal, pero no “regresar” de myFunction hasta que se cumpla la condición.
– Roddy
25 de febrero de 2021 a las 10:06
-
He probado las dos mejores respuestas y esta ha funcionado mejor con diferencia. No solo es el más fácil y limpio, sino que tiene el sentido más lógico y sintáctico. ¡Gracias!
– jack.py
15 de agosto de 2021 a las 14:25
-
Preferiría el operador condicional: conditionFunction() ? resolver() : setTimeout(() => encuesta(resolver), 400)
– Julian
25 de agosto de 2021 a las 11:44
-
Parece una buena solución, pero podría arreglarse un poco: he publicado una respuesta para ordenar.
– Steve Cámaras
23 de mayo a las 15:21
Maciej Dudziński
function waitFor(condition, callback) {
if(!condition()) {
console.log('waiting');
window.setTimeout(waitFor.bind(null, condition, callback), 100); /* this checks the flag every 100 milliseconds*/
} else {
console.log('done');
callback();
}
}
Usar:
waitFor(() => window.waitForMe, () => console.log('got you'))
-
Esta es la única solución que he visto que le permite “esperar” fácilmente una señal, pero no “regresar” de myFunction hasta que se cumpla la condición.
– Roddy
25 de febrero de 2021 a las 10:06
-
He probado las dos mejores respuestas y esta ha funcionado mejor con diferencia. No solo es el más fácil y limpio, sino que tiene el sentido más lógico y sintáctico. ¡Gracias!
– jack.py
15 de agosto de 2021 a las 14:25
-
Preferiría el operador condicional: conditionFunction() ? resolver() : setTimeout(() => encuesta(resolver), 400)
– Julian
25 de agosto de 2021 a las 11:44
-
Parece una buena solución, pero podría arreglarse un poco: he publicado una respuesta para ordenar.
– Steve Cámaras
23 de mayo a las 15:21
Resolví este problema implementando el método a continuación.
const waitUntil = (condition) => {
return new Promise((resolve) => {
let interval = setInterval(() => {
if (!condition()) {
return
}
clearInterval(interval)
resolve()
}, 100)
})
}
Ahora, cada vez que desee esperar hasta que se cumpla una determinada condición, puede llamarlo así.
await waitUntil(() => /* your condition */)
-
Creo que debería ser esperar no esperar en la invocación.
– mega_creamery
15 de diciembre de 2020 a las 14:08
-
@mega_creamery gracias por la corrección. Arreglé el error tipográfico 🙂
– tdxius
18 de diciembre de 2020 a las 20:38
-
En mi opinión, esta fue la solución más limpia y simple.
– Decano
13 de julio de 2021 a las 15:12
Use el patrón de promesa para sus inicializaciones; se puede encontrar en bastantes bibliotecas como
jQuery.Deferred
,Q
,async
…– Sirko
2 de marzo de 2014 a las 9:24
¿dónde exactamente usarlo y cómo?
– ilay zeidman
2 de marzo de 2014 a las 9:24
Hay muchos tutoriales que describen las implementaciones prometedoras de las diversas bibliotecas, por ejemplo. jQuery.Diferido o q. Por cierto, su problema subyacente es el mismo que en esta pregunta.
– Sirko
2 de marzo de 2014 a las 9:30
Para alguien que lea esto en 2018, Promises es compatible con todos los navegadores además de Opera Mini e IE11.
–Daniel Reina
19 de febrero de 2018 a las 19:57
El problema principal es que es imposible hacer una espera de bloqueo (reposo) real en js de un solo subproceso basado en eventos. Solo puede crear un controlador de espera. ver más: stackoverflow.com/questions/41842147/…
– Cerebro saliente
1 oct 2019 a las 13:59