2014-10-21 10 views
5

Ich habe ein unerwartetes Verhalten von JS setTimeout festgestellt, wenn modale Dialogfenster wie alert geöffnet sind und ich möchte den Grund dafür wissen.Warum pausieren JS modale Nachrichtenboxen Countdown auf setTimeout()?

Ich erwartete setTimeout (Fn, 10000) zu "überprüfen Sie die aktuelle Zeit in regelmäßigen Abständen und wenn es größer ist als Now + 10000ms feuern den Event-Handler, der die übergebene" Fn "-Funktion aufrufen wird". Dies wäre logisch, da wir das Timeout-Maß als 'ms ab jetzt' übergeben. Aber anscheinend ist der Countdown auf setTimeout ein wörtliches countdown und wird angehalten, während ein modales Fenster geöffnet ist.

setTimeout(function(){ 
    //alert A 
    alert("10 seconds have passed for the first setTimeout") 
}, 10000); 
setTimeout(function(){ 
    //alert B 
    alert("Wait for 15 seconds and press OK"); 
},1000); 

Ich würde Alarm A erwartet sofort angezeigt werden, nachdem Sie Alarm B schließen (vorausgesetzt, Sie 15 Sekunden lang gewartet., Dies zu tun), da Alarm Ein Timeout nur für 10 Sekunden war, und sie haben bereits vergangen. Die Praxis zeigt jedoch, dass Countdown bis Alarm A einfach unterbrochen ist, während Alarm B offen ist und es erst nach ca. 9 weitere Sekunden sind vergangen, nachdem Sie Alarm B geschlossen haben, egal wie lange B geöffnet war.

Dies scheint nicht logisch.

Aktualisierung. Ich bin definitiv nicht der Einzige, der hier verwirrt ist, weil dieses Verhalten des Anhaltens der Zeitüberschreitung in Chrome und Internet Explorer auftritt, aber nicht in Firefox. Firefox führt das Verhalten aus, das ich erwartet habe - wenn Sie 15 Sekunden auf Alarm B warten, erscheint Alarm A sofort, wenn Sie es schließen.

+0

Dies ist einer der Gründe, warum 'alert's vor allem Funktionen mit Timing nicht gut ist. – soktinpk

+0

@soktinpk Wir verwenden Dinge wie "Bootbox" für Warnungen in unseren Projekten, aber es gibt Zeiten, in denen es nicht anwendbar ist, wie wenn Sie Benutzer über nicht gespeicherte Daten beim Schließen der Seite warnen müssen. Ich kann mir nicht vorstellen, dass Leute wegen dieses Verhaltens zu oft in Schwierigkeiten geraten, aber dennoch ist ein großer Verhaltensunterschied zwischen den drei großen Browsern interessant. –

+0

Warnung ein Benutzer über ungesicherte Daten ist eine vollkommen gültige Verwendung für "Alarm" und/oder "bestätigen". Warum möchten Sie jedoch ein wenig warten, bevor "Alarm" -Shows angezeigt werden? – soktinpk

Antwort

5

Ich bezweifle es, warum IE und Chrome legte eine Pause auf anhängige eine endgültige Antwort ist Timer bis alert wurde entlassen, und Firefox nicht. Ich glaube, es ist nur, weil es gewisse Freiheit bei der Interpretation der W3C's specs for alert:

Die Warnung (Meldung) -Methode, wenn sie aufgerufen wird, müssen die folgenden Schritte ausführen:

  1. Wenn die Beendigung Verschachtelungsebene der Ereignisschleife ist nicht Null, optional Abbruch dieser Schritte.

  2. Den Speicher-Mutex freigeben.

  3. Die angegebene Nachricht dem Benutzer anzeigen.

  4. Optional pausieren Sie, während Sie darauf warten, dass der Benutzer die Nachricht bestätigt.

Schritt 4 (die Pause) erklärt weiter here:

Einige der Algorithmen in dieser Beschreibung, die aus historischen Gründen erfordern den User-Agent zu unterbrechen, während ein Lauf Aufgabe bis eine Bedingung Ziel erfüllt ist. Das bedeutet, die folgenden Schritte ausgeführt werden:

  1. Wenn irgendwelche asynchron laufenden Algorithmen einen stabilen Zustand warten, dann ihren synchronen Abschnitt laufen und dann wieder ihren asynchronen Algorithmus ausgeführt wird. (Siehe event loop Verarbeitungsmodell Definition oben für weitere Details.)

  2. Aktualisieren Sie gegebenenfalls die Wiedergabe oder Benutzeroberfläche jedes Dokument oder Surfen im Kontext der aktuellen Zustand zu reflektieren.

  3. Warten Sie, bis das Bedingungsziel erreicht ist. Während ein Benutzeragent eine pausierte Aufgabe hat, darf die entsprechende Ereignisschleife keine weiteren Aufgaben ausführen, und jedes Skript in der aktuell ausgeführten Aufgabe muss blockieren. Benutzeragenten sollten weiterhin auf Benutzereingaben reagieren, während sie pausiert sind, jedoch in einer reduzierten Kapazität, da die Ereignisschleife nichts unternimmt.

So erhält die Ereignisschleife auf jedem Fall unterbrochen. Der Rückruf des längeren Timeouts wird nicht aufgerufen, solange der Alarm noch sichtbar und modal ist. Wäre es nicht so gewesen, könnten alle Arten von Schädlingen möglich gemacht werden, wie mehrere Warnungen übereinander.

Können Sie nun anhand der oben genannten Spezifikationen feststellen, ob der Countdown des Timers für die gesamte Lebensdauer des Alarms ausgesetzt werden sollte oder eher ausgelöst werden sollte, sobald der Alarm abgelaufen ist? Ich kann nicht, und ich bin mir nicht einmal sicher, welches Verhalten logischer wäre.

Ich bin sicher, dass Sie keine JavaScript-Warnungen für etwas anderes als Debugging-Zwecke verwenden sollten. Warnungen erlauben es, die Skriptausführung zu unterbrechen (während ein asynchroner Vorgang wie XHR im Hintergrund stattfindet), aber sie sind für den Benutzer ziemlich unfreundlich. Der richtige Ansatz wäre asynchrone Codierung, die Verwendung von Versprechen und möglicherweise ES6 generators/yeild (wenn Sie nach dem linearen Code-Stil sind).

Die folgende Frage ist sehr verwandt und einige Alternativen zu alert werden dort diskutiert:

-1

Warnung ist UI-Blockierung und da Javascript single threaded wird es alles vom Ausführen blockiert, bis der Dialog abgewiesen wird.

+0

Sie wollten sagen "Alarm ist synchron". Ich weiß das, aber das beantwortet die Frage nicht. Meine ursprüngliche Erwartung war für Alert A setTimeout die aktuelle Zeit zu überprüfen, direkt nachdem die Funktion in Alert B zurückkehrt und der JS-Thread entsperrt ist, an diesem Punkt würde es sehen, dass über 10000 ms vergangen sind und Alarm ausführen. –

-1

Wenn Sie wirklich brauchen, können Sie Ihren eigenen Timer verwenden.

var now = Date.now(); 
function alertA(){ 
    //alert A 
    alert("10 seconds have passed for the first setTimeout") 
} 
setTimeout(function(){ 
    //alert B 
    alert("Wait for 15 seconds and press OK"); 
    setTimeout(alertA, 10000 - (Date.now() - now)); // Subtract the time passed 
},1000); 

Sie können dies in einem Dienstprogramm Verfahren wickeln:

function alertDelay(messages, timePassed) { 
    if (typeof messages !== "object" || messages.length === 0) return; 
    if (timePassed == null) timePassed = 0; 
    var firstMessage = messages[0]; 
    var now = Date.now(); 
    setTimeout(function() { 
    alert(firstMessage.message); 
    alertDelay(messages.slice(1), Date.now() - now); 
    }, firstMessage.delay - timePassed); 
} 

Verbrauch:

alertDelay([ 
    {message: 'Message 1', delay: 1000}, 
    {message: 'Message 2', delay: 5000}, 
    {message: 'Message 3', delay: 10000} 
]); 
+1

Ich gucke nicht für eine Problemumgehung bin ich nur neugierig, warum. Dieser Code würde definitiv nicht viel helfen, da setTimeouts an verschiedenen Stellen gesetzt sind. Der einzige praktikable Weg, den ich mir von Kopf bis Fuß vorstellen könnte, wäre, window.setTimeout zu überladen, um das schnellste mögliche setInterval inside zu verwenden, eine Liste von Callbacks mit Fälligkeitsterminen zu führen und sie gegen Date.now() zu überprüfen. Sollte funktionieren, aber ich glaube nicht, dass es ein Projekt gibt, das solche Präzision wirklich brauchen würde. Wie auch immer, wie ich schon sagte, mehr interessiert an "warum?" dann "wie repariere ich?". –

+0

@IvanKoshelew Ich denke, dass [dnharjani's answer] (http://stackoverflow.com/a/26487531/3371119) gut erklärt, warum: 'alert' kann den Thread blockieren. Es hängt wirklich von den Implementierungen von "alert" ab. Sie können sich nicht darauf verlassen, wenn Sie Timing-Funktionen verwenden. – soktinpk

+0

Ich glaube nicht, dass das die ursprüngliche Frage beantwortet. Ich weiß, warum es setTimeout-Rückruf verhindert, während es geöffnet ist - keine Überraschung. Was ich nicht verstehe ist, warum es weiter wartet, nachdem es geschlossen wurde. Warum nur FireFox es so macht, wie ich es erwartet habe? Soll ich das als Bug an andere Browser Bugtracker übermitteln? Ich meine, da sich die mit dem gleichen Code verhalten - einer von ihnen muss falsch liegen. Die Frage ist, welcher und warum? –