Sollte ein Ereignis-Listener aufgerufen werden, wenn er angehängt ist, nachdem das Ereignis bereits ausgelöst wurde?

8

Sollte ein Ereignis-Listener aufgerufen werden, wenn er angehängt ist, nachdem das Ereignis bereits ausgelöst wurde? Was ist, wenn das Ereignis nur einmal ausgelöst wird?

Das erste Beispiel, das mir in den Sinn kommt, ist das readyEreignis in jQuery. Das folgende Snippet ruft nach dem Laden der Seite den Rückruf auf:

$(document).ready(function () {
    console.log("Works.");
});

Die Alternative zu diesem Verhalten könnte ein Flag sein, das beim Laden der Seite gesetzt wird und den Verbraucher der API dazu zwingt, zu überprüfen, ob das Ereignis bereits eingetreten ist, und entsprechend zu handeln:

if (document.readyState == "complete") {
    console.log("Works.");
} else {
    $(document).ready(function () {
        console.log("Works.");
    });
}

Während das obige Beispiel im Zusammenhang mit dem Laden einer Webseite steht, bei der (normalerweise) alles und jedes geschehen muss, nachdem die Seite vollständig geladen wurde, können dieselben Argumente für jede einzelne Komponente in einer Anwendung mit "Singleton" -Ereignissen vorgebracht werden ( load, start, end, usw.). Ein Beispiel für eine einzelne Komponente könnte eine Karte mit einem loadEreignis sein , das ausgelöst wird, um anzugeben, dass die Karte geladen wurde :

map.on("load", function () {
    console.log("The map has loaded.");
});

Das obige Beispiel stammt aus der ArcGIS-API für JavaScript, in der dieses Ereignis nur einmal ausgelöst wird. Wenn ein Verbraucher darauf wartet, dass die Karte geladen wird, nachdem die Karte bereits geladen wurde, wird der Listener niemals aufgerufen. Für das gewünschte Ergebnis muss der Status der Karte überprüft werden, bevor der Listener angehängt wird:

if (map.loaded) {
    console.log("The map has loaded.");
} else {
    map.on("load", function () {
        console.log("The map has loaded.");
    });
}

Welches Verhalten ist korrekt, wenn der Verbraucher überprüfen muss, ob ein Ereignis bereits ausgelöst wurde, oder immer den Rückruf aufruft?

Whymarrh
quelle
5
Sie könnten an Reactive Extensions Subjectvs. interessiert sein ReplaySubject, bei denen letzterer frühere Ereignisse für späte Abonnenten wiedergibt, während ersterer dies nicht tut. Das heißt, die Entwickler von Rx haben beide Verhaltensweisen modelliert, anstatt sich für ein bestimmtes Verhalten zu entscheiden. - Während die obigen Links zur Dokumentation der .NET-Version von Rx führen, gibt es auch einen Rx für JavaScript .
Stakx

Antworten:

19

Das hängt davon ab, ob es sich wirklich um eine Ereignis- oder Statusbenachrichtigung handelt (was bereit ist). Wenn es sich um ein Ereignis handelt, informieren Sie den Listener nicht über alle vorherigen Ereignisse. Wenn es sich um eine Statusbenachrichtigung handelt, teilen Sie dies ihnen sofort mit, nachdem sie sich angemeldet haben und Sie sich im angegebenen Status befinden.

Das Knifflige wird sein, wo es entweder ein Zustand oder ein Ereignis sein kann, je nachdem, wie Sie es betrachten - im schlimmsten Fall wird dies durch die Dokumentation erklärt. Bestenfalls wählen Sie einen guten Namen, der klar macht, was Sie meinen.

jmoreno
quelle
2
+1, auch zur Erwähnung von Statusmeldungen. Staatliche Benachrichtigungen sind IMO in der Nähe von Futures und deren Behandlung von Rückrufen, z. B. in Scala ( docs.scala-lang.org/overviews/core/futures.html ).
Giorgio
Interessant, daran hatte ich nicht gedacht. Würden Sie klassifizieren load, startoder enddie gleiche wie readyals Zustand Meldung?
Whymarrh
@Whymarrh: Das hängt vom Kontext ab, aber das klingt für mich nach Ereignissen. Ich würde dazu neigen, die Statusbenachrichtigung als Fragen zu bezeichnen ... bereit? gestartet? Laufen? Aber natürlich müssen Sie es möglicherweise in eine vorhandene Namenskonvention einpassen.
Jmoreno
3
Mir scheint, dass Ereignisse als etwas interpretiert werden können, das einmal auftritt, um den Zustand zu ändern. Dies bedeutet, dass sie sehr kurzlebig sind, praktisch ein einziger Moment. Ein Zustand hingegen ist längerlebig und besteht aus mehreren Momenten. loadist weder ein Ereignis noch ein Staat. loadingStartedist ein Ereignis und loadingist ein Staat.
Jeroen Vannevel
5

Im Allgemeinen nein .

Ereignis-Listener sollten nicht aufgerufen werden, wenn sie angehängt sind, nachdem das Ereignis bereits ausgelöst wurde. "Wenn du einschläfst, verlierst du."

Es gibt einige wichtige Ausnahmen. $(document).ready()ist vielleicht das perfekte Beispiel - ein Ereignis, vor dem der gesamte Bewertungskontext nicht zuverlässig oder vollständig hergestellt ist und das als Marker "Vollfunktionsverarbeitung beginnt hier" dient.

Wenn Sie jedoch die Regel festlegen, dass ein Ereignishandler auch dann ausgelöst werden soll, wenn er nach dem Eintreten des Ereignisses installiert und / oder aktiviert wird, haben Sie erklärt, dass alle Ereignisse vom Beginn bis zum Ende des Programmlaufs gepuffert werden sollen oder müssen. für jedes denkbare Ereignis. Andernfalls werden möglicherweise Ereignisbehandlungsroutinen für ein Ereignis eingerichtet, das irgendwo in der Zukunft nicht über ausreichende Informationen verfügt, um zu wissen, ob Sie bei der Einrichtung feuern sollten. Das bedeutet, dass Sie jede mögliche Ereignisquelle instrumentieren und auch jede von ihnen unendlich puffern müssen. Dies führt die Ereignisverarbeitung von der verzögerten Bewertung (und all ihren Leistungsvorteilen) bis zur eifrigen Bewertungund auf alles, was sich am anderen Ende des Spektrums befindet. Frenetische Bewertung basierend auf katastrophalen Annahmen darüber, welche Ereignisse später behandelt werden müssen?

Ereignisse sind in der Regel vorübergehend und zahlreich. Das Festlegen von Regeln, nach denen die einzelnen Regeln ohne Einschränkung gepuffert werden müssen, um die Möglichkeit zu gewährleisten, dass sie irgendwann abgerufen werden - das ist Leistungsselbstmord. Darüber hinaus besteht keine große Voraussetzung dafür, da Handler in der Regel früh im Programmleben eingerichtet werden können.

Ausnahmen von der Regel sind Randfälle - beispielsweise das Laden von Dokumenten oder Subsystemen, bei denen es sich um wesentliche, eindeutige Großereignisse handelt, die ansonsten nicht erfassbar oder handhabbar sind. Dies ist sehr nah an Ihren "Singleton" -Ereignissen. Eine andere Gruppe, die möglicherweise eine besondere Behandlung benötigt, sind erhebliche Fehler, Sicherheitsereignisse oder Statusänderungen, die in Ausnahmefällen gekennzeichnet werden müssen, selbst wenn zum Zeitpunkt des Ereignisses keine Abonnenten für das Kennzeichen vorhanden sind.

Ein letzter Hinweis: Ein "Bereit" -Ereignis unterscheidet sich geringfügig von einem "Lade" -Ereignis. Obwohl sie oft nicht klar voneinander unterschieden sind (z. B. HTML onloadund jQuery $(document).ready()als logisch sehr ähnlich angesehen werden) und beide die Verfügbarkeit einer Ressource oder Verarbeitungsumgebung signalisieren, sind sie nicht ganz gleich. Das Laden (oder das Beenden davon) ist ein echtes Ereignis - etwas, das von der Infrastruktur an den Verbraucher signalisiert wird. Bereitschaft ist jedoch eher das RendezvousWenn die Infrastruktur die Ressource / Umgebung lädt und der Verbraucher bereit ist, diese Verfügbarkeit zu nutzen. Die Bereitschaft erfolgt nach dem Laden und ist eines der festgelegten Sonderereignisse / Koordinierungspunkte, die als Warteschlange behandelt werden müssen, da Sie sie nicht auf andere Weise erfassen können. Dass es einige ganz besondere Fälle gibt, bedeutet jedoch nicht, dass jedes Ereignis a posteriori ausgelöst werden sollte .

Jonathan Eunice
quelle
Ähm, meintest du HTML's "Onload"?
StarWeaver
@ StarWeaver Danke. Ich bin mir nicht sicher, ob ich ausgerutscht bin oder meine überambitionierte Autokorrektur es getan hat, aber Sie haben völlig Recht. Fest.
Jonathan Eunice
NP, ich bin nicht ganz mit HTML Post 4 fertig, aber es klang seltsam. Schöne Erklärung auch.
StarWeaver