J'ai une fonction javascript comme celle-ci:
function myFunction(number) {
var x=number;
...
... more initializations
//here need to wait until flag==true
while(flag==false)
{}
...
... do something
}
Le problème est que le javascript est bloqué pendant un certain temps et bloqué mon programme. donc ma question est de savoir comment puis-je attendre au milieu de la fonction jusqu'à ce que l'indicateur soit vrai sans "busy-wait"?
javascript
synchronization
ilay zeidman
la source
la source
jQuery.Deferred
,Q
,async
...Réponses:
Étant donné que javascript dans un navigateur est à thread unique (sauf pour les webworkers qui ne sont pas impliqués ici) et qu'un thread d'exécution de javascript s'exécute jusqu'à la fin avant qu'un autre puisse s'exécuter, votre déclaration:
fonctionnera simplement pour toujours (ou jusqu'à ce que le navigateur se plaint d'une boucle javascript non réactive), la page semblera bloquée et aucun autre javascript n'aura jamais la possibilité de s'exécuter, la valeur de l'indicateur ne pourra donc jamais être modifiée.
Pour un peu plus d'explications, Javascript est un langage événementiel . Cela signifie qu'il exécute un morceau de Javascript jusqu'à ce qu'il renvoie le contrôle à l'interpréteur. Ensuite, seulement lorsqu'il revient à l'interpréteur, Javascript récupère l'événement suivant de la file d'attente d'événements et l'exécute.
Toutes les choses comme les minuteries et les événements réseau passent par la file d'attente des événements. Ainsi, lorsqu'une minuterie se déclenche ou qu'une requête réseau arrive, elle n'interrompt jamais le Javascript en cours d'exécution. Au lieu de cela, un événement est placé dans la file d'attente d'événements Javascript, puis, lorsque le Javascript en cours d'exécution se termine, l'événement suivant est extrait de la file d'attente d'événements et il est exécuté à son tour.
Ainsi, lorsque vous effectuez une boucle infinie telle que
while(flag==false) {}
, le Javascript en cours d'exécution ne se termine jamais et donc l'événement suivant n'est jamais extrait de la file d'attente d'événements et donc la valeur deflag
ne change jamais. La clé ici est que Javascript n'est pas piloté par interruption . Lorsqu'un minuteur se déclenche, il n'interrompt pas le Javascript en cours d'exécution, exécute un autre Javascript et laisse le Javascript en cours d'exécution continuer. Il est simplement placé dans la file d'attente d'événements en attendant que le Javascript en cours d'exécution soit terminé pour s'exécuter à son tour.Ce que vous devez faire est de repenser le fonctionnement de votre code et de trouver une manière différente de déclencher le code que vous souhaitez exécuter lorsque la
flag
valeur change. Javascript est conçu comme un langage événementiel. Donc, ce que vous devez faire est de déterminer les événements pour lesquels vous pouvez enregistrer un intérêt afin que vous puissiez soit écouter l'événement qui pourrait provoquer le changement du drapeau et vous pouvez examiner le drapeau sur cet événement ou vous pouvez déclencher votre propre événement à partir de quel que soit le code qui pourrait changer l'indicateur ou vous pouvez implémenter une fonction de rappel que quel que soit le code qui change, cet indicateur peut appeler votre rappel chaque fois que le morceau de code responsable de la modification de la valeur de l'indicateur changerait sa valeur entrue
, il appelle simplement la fonction de rappel et donc votre code qui veut s'exécuter lorsque l'indicateur est défini surtrue
arrivera à fonctionner au bon moment. C'est beaucoup plus efficace que d'essayer d'utiliser une sorte de minuterie pour vérifier constamment la valeur de l'indicateur.la source
Javascript est mono-thread, d'où le comportement de blocage de page. Vous pouvez utiliser l'approche différée / promesse suggérée par d'autres, mais la manière la plus élémentaire serait de l'utiliser
window.setTimeout
. Par exempleVoici un bon tutoriel avec des explications supplémentaires: Tutoriel
ÉDITER
Comme d'autres l'ont souligné, le meilleur moyen serait de restructurer votre code pour utiliser des rappels. Cependant, cette réponse devrait vous donner une idée de la façon dont vous pouvez «simuler» un comportement asynchrone avec
window.setTimeout
.la source
Utilisation:
la source
Solution utilisant Promise , async \ await et EventEmitter qui permet de réagir immédiatement au changement de drapeau sans aucun type de boucle
EventEmitter
est intégré au node. Dans le navigateur, vous devrez l'inclure vous-même, par exemple en utilisant ce package: https://www.npmjs.com/package/eventemitter3la source
ES6 avec Async / Await,
la source
Avec Ecma Script 2017, vous pouvez utiliser async-await et tout ensemble pour le faire Et bien que ne plantera pas ou ne verrouille pas le programme, même la variable ne sera jamais vraie
la source
Solution moderne utilisant Promise
myFunction()
dans la question d'origine peut être modifié comme suitoù
until()
est cette fonction utilitaireCertaines références aux fonctions async / await et arrow sont dans un article similaire: https://stackoverflow.com/a/52652681/209794
la source
Pour itérer sur des objets ($ .each) et exécuter une opération de longue durée (contenant des appels de synchronisation ajax imbriqués) sur chaque objet:
J'ai d'abord défini une
done=false
propriété personnalisée sur chacun.Ensuite, dans une fonction récursive, définissez chacun
done=true
et continuez à utilisersetTimeout
. (C'est une opération destinée à arrêter toutes les autres interfaces utilisateur, afficher une barre de progression et bloquer toute autre utilisation, donc je me suis pardonné pour les appels de synchronisation.)la source
Semblable à la réponse de Lightbeard, j'utilise l'approche suivante
la source
J'ai essayé d'utiliser l'approche @Kiran comme suit:
(le cadre que j'utilise me force à définir les fonctions de cette façon). Mais sans succès car lorsque l'exécution entre dans la fonction checkFlag la deuxième fois, ce
this
n'est pas mon objetWindow
. Donc, j'ai fini avec le code ci-dessousla source
utilisation de javascript non bloquant avec l' API EventTarget
Dans mon exemple, je dois attendre un rappel avant de l'utiliser. Je n'ai aucune idée du moment où ce rappel est défini. Cela peut être avant ou après avoir besoin de l'exécuter. Et je peux avoir besoin de l'appeler plusieurs fois (tout est asynchrone)
la source
il existe un package de nœuds
delay
très facile à utiliserla source
J'ai adopté une approche similaire aux solutions de rappel ici, mais j'ai essayé de la rendre un peu plus générique. L'idée est que vous ajoutez des fonctions que vous devez exécuter après que quelque chose change dans une file d'attente. Lorsque cela se produit, vous parcourez la file d'attente, appelez les fonctions et videz la file d'attente.
Ajouter une fonction à la file d'attente:
Exécutez et videz la file d'attente:
Et lorsque vous appelez _addToQueue, vous voudrez encapsuler le rappel:
Lorsque vous avez rempli la condition, appelez
_runQueue()
Cela m'a été utile car j'avais plusieurs choses à attendre dans la même condition. Et il dissocie la détection de la condition de tout ce qui doit être exécuté lorsque cette condition est atteinte.
la source
la source
Dans mon exemple, j'enregistre une nouvelle valeur de compteur toutes les secondes:
la source
Si vous êtes autorisé à utiliser:
async/await
sur votre code, vous pouvez essayer celui-ci:Démo ici: https://stackblitz.com/edit/typescript-bgtnhj?file=index.ts
Sur la console, il suffit de copier / coller:
goahead = true
.la source