Wie kann man warten, bis eine Prädikatbedingung in JavaScript wahr wird?

Lesezeit: 8 Minuten

Benutzeravatar von ilay zeidman
Ilay Zeidman

Ich habe eine Javascript-Funktion wie folgt:

function myFunction(number) {

    var x=number;
    ...
    ... more initializations
    //here need to wait until flag==true
    while(flag==false)
    {}

    ...
    ... do something

}

Das Problem ist, dass das Javascript in der Zwischenzeit hängen bleibt und mein Programm hängen bleibt. Meine Frage ist also, wie ich mitten in der Funktion warten kann, bis das Flag ohne “busy-wait” wahr ist.

  • Verwenden Sie das Promise-Muster für Ihre Initialisierungen – kann in einigen Bibliotheken gefunden werden, z jQuery.Deferred, Q, async

    – Sirko

    2. März 2014 um 9:24 Uhr

  • wo genau und wie?

    – Ilay Zeidman

    2. März 2014 um 9:24 Uhr

  • Es gibt viele Tutorials, die die Promise-Implementierungen der verschiedenen Bibliotheken beschreiben, z. jQuery.Deferred oder Q. Übrigens ist Ihr zugrunde liegendes Problem dasselbe wie in dieser Frage.

    – Sirko

    2. März 2014 um 9:30 Uhr


  • Für jemanden, der dies 2018 liest, Promises werden von allen Browsern außer Opera Mini und IE11 unterstützt.

    – Daniel Reina

    19. Februar 2018 um 19:57 Uhr

  • Das Hauptproblem besteht darin, dass es unmöglich ist, in Event-Diven-Singlethread-js wirklich zu blockieren (sleep) zu warten. Sie können nur einen Wait-Handler erstellen. mehr sehen: stackoverflow.com/questions/41842147/…

    – Herausragendes Gehirn

    1. Oktober 2019 um 13:59 Uhr


Benutzeravatar von Kiran
Kiran

Javascript ist Single-Threaded, daher das Seitenblockierungsverhalten. Sie können den von anderen vorgeschlagenen aufgeschobenen/versprochenen Ansatz verwenden. Der einfachste Weg wäre die Verwendung window.setTimeout. Z.B

function checkFlag() {
    if(flag === false) {
       window.setTimeout(checkFlag, 100); /* this checks the flag every 100 milliseconds*/
    } else {
      /* do something*/
    }
}
checkFlag();

Hier ist ein gutes Tutorial mit weiteren Erklärungen: Lernprogramm

BEARBEITEN

Wie andere bereits betonten, wäre es am besten, Ihren Code so umzustrukturieren, dass er Rückrufe verwendet. Diese Antwort sollte Ihnen jedoch eine Vorstellung davon geben, wie Sie ein asynchrones Verhalten “simulieren” können window.setTimeout.

  • Während ich diese Antwort einerseits sehr mag, weil es sich tatsächlich um ein js-‘Warten’ handelt, wird sie nicht so brauchbar, wenn Sie einen Wert zurückgeben möchten. Wenn Sie keinen Wert zurückgeben, bin ich mir nicht sicher, ob es einen realen Anwendungsfall für das Muster gibt?

    – Martin Meerer

    18. April 2016 um 19:54 Uhr

  • Sie können natürlich ein Versprechen zurückgeben und die Funktion so implementieren. Dies würde jedoch im Allgemeinen eine Bibliothek eines Drittanbieters erfordern, die Promises oder Polyfill implementiert, es sei denn, Sie verwenden ECMA-262. Ohne ein Promise zurückzugeben, ist es am besten, einen Callback-Mechanismus zu verwenden, um dem Aufrufer zu signalisieren, dass ein Ergebnis verfügbar ist.

    – Kiran

    4. November 2016 um 4:21 Uhr

  • Sie können bei Bedarf auch Parameter übergeben: stackoverflow.com/questions/1190642/…

    – SharpC

    20. September 2017 um 8:00 Uhr

  • Das ist so eine tolle Antwort. Ich wollte fast aufgeben, nachdem ich mich durch viele Tech-Foren gegraben hatte. Ich denke, es hat perfekt für mich funktioniert, weil ich einen Wert zurückgegeben habe. Es funktionierte auch im Internet Explorer. Vielen Dank.

    – Joseph

    2. Oktober 2019 um 20:02 Uhr

  • Wenn wir aus irgendeinem Grund Parameter an die checkFlag-Funktion senden müssen, müssen wir eine anonyme/Pfeilfunktion verwenden, wie z. window.setTimeout( () => { checkFlag(params); }, 100);

    – Said

    16. Februar 2021 um 14:00 Uhr


Benutzeravatar von jfriend00
jfriend00

Da Javascript in einem Browser Single-Threaded ist (mit Ausnahme von Webworkern, die hier nicht beteiligt sind) und ein Thread der Javascript-Ausführung vollständig ausgeführt wird, bevor ein anderer ausgeführt werden kann, lautet Ihre Aussage:

while(flag==false) {}

einfach für immer ausgeführt wird (oder bis sich der Browser über eine nicht reagierende Javascript-Schleife beschwert), scheint die Seite hängen zu bleiben und kein anderes Javascript wird jemals die Chance bekommen, ausgeführt zu werden, daher kann der Wert des Flags niemals geändert werden.

Für eine etwas mehr Erklärung, Javascript ist eine ereignisgesteuerte Sprache. Das bedeutet, dass es ein Stück Javascript ausführt, bis es die Kontrolle wieder an den Interpreter zurückgibt. Erst wenn es zum Interpreter zurückkehrt, holt Javascript das nächste Ereignis aus der Ereigniswarteschlange und führt es aus.

Alle Dinge wie Timer und Netzwerkereignisse laufen durch die Ereigniswarteschlange. Wenn also ein Timer ausgelöst wird oder eine Netzwerkanfrage eintrifft, wird das derzeit ausgeführte Javascript niemals “unterbrochen”. Stattdessen wird ein Ereignis in die Javascript-Ereigniswarteschlange gestellt und dann, wenn das aktuell ausgeführte Javascript beendet ist, wird das nächste Ereignis aus der Ereigniswarteschlange gezogen und es ist an der Reihe, es auszuführen.

Wenn Sie also eine Endlosschleife ausführen, z while(flag==false) {}wird das aktuell laufende Javascript niemals beendet und somit wird das nächste Ereignis niemals aus der Ereigniswarteschlange und damit dem Wert von gezogen flag wird nie gewechselt. Der Schlüssel hier ist das Javascript ist nicht interruptgesteuert. Wenn ein Timer ausgelöst wird, unterbricht er nicht das gerade laufende Javascript, führt ein anderes Javascript aus und lässt dann das gerade laufende Javascript weiterlaufen. Es wird einfach in die Ereigniswarteschlange gestellt und wartet, bis das derzeit ausgeführte Javascript fertig ist, um ausgeführt zu werden.


Was Sie tun müssen, ist zu überdenken, wie Ihr Code funktioniert, und einen anderen Weg zu finden, um den Code auszulösen, den Sie ausführen möchten, wenn der flag Wert ändert. Javascript ist als ereignisgesteuerte Sprache konzipiert. Was Sie also tun müssen, ist herauszufinden, an welchen Ereignissen Sie Interesse anmelden können, damit Sie entweder auf das Ereignis lauschen können, das dazu führen könnte, dass sich das Flag ändert, und Sie können das Flag dieses Ereignisses untersuchen oder Ihr eigenes Ereignis auslösen Welcher Code auch immer das Flag ändern könnte, oder Sie können eine Rückruffunktion implementieren, mit der jeder Code, der dieses Flag ändert, Ihren Rückruf aufrufen kann, wenn der Code, der für die Änderung des Flag-Werts verantwortlich ist, seinen Wert ändern würde truees ruft nur die Callback-Funktion und damit Ihren Code auf, der ausgeführt werden soll, wenn das Flag auf gesetzt wird true wird zur richtigen Zeit laufen. Das ist viel, viel effizienter, als zu versuchen, eine Art Timer zu verwenden, um den Flag-Wert ständig zu überprüfen.

function codeThatMightChangeFlag(callback) {
    // do a bunch of stuff
    if (condition happens to change flag value) {
        // call the callback to notify other code
        callback();
    }
}

Benutzeravatar von Yaroslav Dobzhanskij
Jaroslaw Dobzhanskij

Lösung verwenden Versprechenasync\await und EventEmitter was es ermöglicht, ohne jegliche Schleifen sofort auf Flag-Änderungen zu reagieren

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 ist im Knoten eingebaut. Im Browser müssen Sie es selbst einbinden, zum Beispiel mit diesem Paket: https://www.npmjs.com/package/eventemitter3

  • Ein Beispiel dafür, wie dieser Code verwendet werden kann: (1) Zunächst lock ist falsch. (2) Einige Codeaufrufe lockable. Dieser Code wertet aus if (lock) zu false, also geht es weiter: es setzt lock zu wahr, fährt dann fort, einige Logik auszuführen. In der Zwischenzeit: (3) Einige andere Codeaufrufe lockable. Dieser Code wertet jedoch aus if (lock) zu wahr, also wartet man auf das Versprechen, bis ein unlocked Ereignis wird ausgegeben. (4) Zurück zum ersten aufrufenden Code: Er beendet seine Logik, setzt lock auf false und gibt ein aus unlocked Veranstaltung. (5) Der andere Code kann nun seine Ausführung fortsetzen.

    – OfirD

    8. Juni 2021 um 18:23 Uhr

ES6 mit 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)

Moderne Lösung mit Promise

myFunction() in der ursprünglichen Frage kann wie folgt geändert werden

async function myFunction(number) {

    var x=number;
    ...
    ... more initializations

    await until(_ => flag == true);

    ...
    ... do something

}

wo until() ist diese Nutzenfunktion

function until(conditionFunction) {

  const poll = resolve => {
    if(conditionFunction()) resolve();
    else setTimeout(_ => poll(resolve), 400);
  }

  return new Promise(poll);
}

Einige Verweise auf async/await- und Pfeilfunktionen befinden sich in einem ähnlichen Beitrag: https://stackoverflow.com/a/52652681/209794

  • Dies ist die einzige Lösung, die ich sehe, mit der Sie problemlos auf ein Signal “warten” können, aber nicht von einer myFunction “zurückkehren”, bis die Bedingung erfüllt ist.

    – Roddy

    25. Februar 2021 um 10:06 Uhr

  • Ich habe die besten Antworten ausprobiert und diese hat bei weitem am besten funktioniert. Es ist nicht nur am einfachsten und saubersten, sondern auch am logischsten und syntaktisch sinnvollsten. Vielen Dank!

    – jack.py

    15. August 2021 um 14:25 Uhr

  • Ich würde den Bedingungsoperator bevorzugen: conditionFunction() ? resolve() : setTimeout(() => poll(resolve), 400)

    – Julian

    25. August 2021 um 11:44 Uhr


  • Scheint eine gute Lösung zu sein, könnte aber leicht aufgeräumt werden – habe eine Antwort zum Aufräumen gepostet.

    – Steve Kammern

    23. Mai um 15:21 Uhr

Benutzeravatar von Maciej Dudziński
Maciej Dudzinski

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();
    }
}

Verwenden:

waitFor(() => window.waitForMe, () => console.log('got you'))

  • Dies ist die einzige Lösung, die ich sehe, mit der Sie problemlos auf ein Signal “warten” können, aber nicht von einer myFunction “zurückkehren”, bis die Bedingung erfüllt ist.

    – Roddy

    25. Februar 2021 um 10:06 Uhr

  • Ich habe die besten Antworten ausprobiert und diese hat bei weitem am besten funktioniert. Es ist nicht nur am einfachsten und saubersten, sondern auch am logischsten und syntaktisch sinnvollsten. Vielen Dank!

    – jack.py

    15. August 2021 um 14:25 Uhr

  • Ich würde den Bedingungsoperator bevorzugen: conditionFunction() ? resolve() : setTimeout(() => poll(resolve), 400)

    – Julian

    25. August 2021 um 11:44 Uhr


  • Scheint eine gute Lösung zu sein, könnte aber leicht aufgeräumt werden – habe eine Antwort zum Aufräumen gepostet.

    – Steve Kammern

    23. Mai um 15:21 Uhr

Ich habe dieses Problem gelöst, indem ich die folgende Methode implementiert habe.

const waitUntil = (condition) => {
    return new Promise((resolve) => {
        let interval = setInterval(() => {
            if (!condition()) {
                return
            }

            clearInterval(interval)
            resolve()
        }, 100)
    })
}

Wenn Sie jetzt warten möchten, bis eine bestimmte Bedingung erfüllt ist, können Sie es so nennen.

await waitUntil(() => /* your condition */)

  • Ich denke, es sollte im Aufruf warten, nicht warten

    – mega_creamery

    15. Dezember 2020 um 14:08 Uhr

  • @mega_creamery danke für die Korrektur. Ich habe den Tippfehler korrigiert 🙂

    – tdxius

    18. Dezember 2020 um 20:38 Uhr

  • Das war meiner Meinung nach die sauberste und einfachste Lösung

    – Dekan

    13. Juli 2021 um 15:12 Uhr

1412330cookie-checkWie kann man warten, bis eine Prädikatbedingung in JavaScript wahr wird?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy