Ein Grund dafür ist, den Click-Handler laufen zu lassen, ohne die Seite einzufrieren. JavaScript ist single threaded. Wenn der Klick-Handler (f
) eine lange Zeit benötigt, um ausgeführt zu werden (möglicherweise macht er eine Berechnung, die etwa 1 Minute dauert), sind alle Klicks/Aktionen, die der Benutzer auf dem Bildschirm ausführt, added to a event queue. Der Browser führt diese Aktionen aus, sobald der Click-Handler abgeschlossen ist. Aber bis dahin scheint die App für den Benutzer nicht mehr zu reagieren - er klickt auf Schaltflächen, aber nichts scheint zu passieren.
setTimeout
hilft, weil der Click-Handler sofort zurückkehrt. Der Callback f
wird zur Ereigniswarteschlange hinzugefügt, um in einem zukünftigen Turn ausgeführt zu werden, was dem Browser die Möglichkeit gibt, alle bereits in der Queue vorhandenen Events auszuführen (z. B. kann die Button-Animation des Herunterdrückens und Wiederkommens eine Chance haben, verarbeitet zu werden) Wenn Sie einen lang laufenden Click-Handler haben, bleibt der Button gedrückt.
Während dies besser ist, gibt es immer noch eine Chance, dass die Seite eingefroren wird, wenn f
ausgeführt wird. Der Weg, dies zu vermeiden, würde für f
seine Arbeit in kleinen Stücken erledigen, d. H. Etwas für etwa 20 ms tun, und dann setTimeout mit einem Rückruf für den Rest der Arbeit aufrufen. Auf diese Weise haben alle anderen Ereignisse, die sich während dieser 20 ms angesammelt haben, eine Chance zu laufen. Eine solche Funktion könnte wie folgt aussehen:
something.click(safeWrap(function() {
function part1() { /* do something */ }
function part2() { /* do something */ }
part1();
safeWrap(part2);
}));
Ein weiterer Grund, dies zu tun wäre, um nicht eine Ausnahme zu lassen, die bis zum Klick-Methode in der Klick-Handler Blase passieren könnte. Vielleicht fängt die Methode click
automatisch Ausnahmen ab und zeigt sie auf dem Bildschirm an, und in diesem speziellen Fall möchten wir, dass der Fehler unbemerkt bleibt. Das funktioniert, weil f
in einer anderen Runde der Ereignisschleife ausgeführt wird und Ausnahmen nicht den Stapel auf click
gehen, da der Aufrufstapel in dem neuen Zug mit f
beginnt. Die Ausnahme würde Blase bis zur click
Methode, wenn die Prozedur nicht in safeWrap
weil in diesem Fall eingewickelt wird, würde f
ein regelmäßiger Methodenaufruf sein von click
Schließlich wird angemerkt, dass der Click-Handler auch nach sicherer Verpackung bekommt all die Argumente, die die click
-Methode zu ihm übergibt. Es tut nicht erhalten die zusätzlichen Argumente an die safeWrap
übergeben, wie in der anderen Antwort hingewiesen. Dies ist offensichtlich sinnvoll, da der Click-Handler nicht darauf achten soll, ob der Handler direkt übergeben oder sicher umschlossen wird.Dieser Code macht es wahrscheinlich klar:
(function() {
console.clear();
function safeWrap(f) {
return function() {
setTimeout.apply(window, [f, 0].concat([].slice.call(arguments)));
};
}
var f = function(a) { console.log(a /* prints 2 */); throw Error("f"); };
function click(f) {
f(2);
}
click(safeWrap(f, 1));
})();
Danke für Ihre Antwort, Ronald. Können Sie näher erläutern, warum ".call" auf ".slice" aufgerufen wird? Ich bin sehr verwirrt über diesen Teil. –
@Kyle, [] .slice.call (Argumente) wird verwendet, um das arguments-Objekt in ein Array-Objekt umzuwandeln. Das arguments-Objekt wird als array-ähnliches Objekt bezeichnet (es hat eine length-Eigenschaft und einige Eigenschaften verwenden die Zahl für ihre Namen). Das Aufrufen von Slice im Kontext von Argumenten macht den Rückgabewert zu einem Array, das diese nummerierten Eigenschaften in Argumenten enthält. –
Die Methode "call" führt die Funktion mit den angegebenen Argumenten aus, sodass Sie ihren Rückgabewert (ein Array) und nicht die Funktionsreferenz erhalten. – Ronald