Die Idee eines „abbrechbaren“ Fetch kam 2017 auf, als AbortController veröffentlicht wurde. Dies gibt uns die Möglichkeit, eine API-Anfrage, die von fetch() initiiert wurde – sogar mehrere Aufrufe – jederzeit abzubrechen.
Hier ist ein super einfaches Beispiel, das AbortController verwendet, um eine fetch()-Anfrage abzubrechen
const controller = new AbortController();
const res = fetch('/', { signal: controller.signal });
controller.abort();
console.log(res); // => Promise(rejected): "DOMException: The user aborted a request"
Seinen Wert sieht man wirklich, wenn er für eine moderne Schnittstelle von setTimeout verwendet wird. Auf diese Weise ist es ziemlich einfach, ein Fetch mit einem Timeout von sagen wir 10 Sekunden zu versehen.
function timeout(duration, signal) {
return new Promise((resolve, reject) => {
const handle = setTimeout(resolve, duration);
signal?.addEventListener('abort', e => {
clearTimeout(handle);
reject(new Error('aborted'));
});
});
}
// Usage
const controller = new AbortController();
const promise = timeout(10000, controller.signal);
controller.abort();
console.log(promise); // => Promise(rejected): "Error: aborted"
Aber die große Neuigkeit ist, dass addEventListener seit Chrome 88 ein Abort Signal akzeptiert. Was ist daran cool? Es kann als Alternative zu removeEventListener verwendet werden.
const controller = new AbortController();
eventTarget.addEventListener('event-type', handler, { signal: controller.signal });
controller.abort();
Was ist *noch* cooler? Nun, da AbortController in der Lage ist, mehrere abbrechbare Anfragen auf einmal abzubrechen, strafft es den Prozess des Entfernens mehrerer Listener auf einmal. Ich habe es bereits für Drag-and-Drop als besonders nützlich empfunden.
Hier ist, wie ich ein Drag-and-Drop-Skript ohne AbortController geschrieben hätte, wobei zwei removeEventListener-Instanzen zum Löschen zweier verschiedener Ereignisse verwendet werden.
// With removeEventListener
el.addEventListener('mousedown', e => {
if (e.buttons !== 1) return;
const onMousemove = e => {
if (e.buttons !== 1) return;
/* work */
}
const onMouseup = e => {
if (e.buttons & 1) return;
window.removeEventListener('mousemove', onMousemove);
window.removeEventListener('mouseup', onMouseup);
}
window.addEventListener('mousemove', onMousemove);
window.addEventListener('mouseup', onMouseup); // Can’t use `once: true` here because we want to remove the event only when primary button is up
});
Mit dem neuesten Update akzeptiert addEventListener die Eigenschaft signal als zweites Argument, wodurch wir abort() einmal aufrufen können, um alle Event-Listener zu stoppen, wenn sie nicht mehr benötigt werden.
// With AbortController
el.addEventListener('mousedown', e => {
if (e.buttons !== 1) return;
const controller = new AbortController();
window.addEventListener('mousemove', e => {
if (e.buttons !== 1) return;
/* work */
}, { signal: controller.signal });
window.addEventListener('mouseup', e => {
if (e.buttons & 1) return;
controller.abort();
}, { signal: controller.signal });
});
Auch hier ist Chrome 88 derzeit der einzige Ort, an dem addEventListener offiziell ein AbortSignal akzeptiert. Während andere große Browser, einschließlich Firefox und Safari, AbortController unterstützen, ist die Integration seines Signals mit addEventListener derzeit nicht möglich… und es gibt keine Signale (Wortspiel beabsichtigt), dass sie daran arbeiten werden. Dennoch ist ein Polyfill verfügbar.
Hey, das ist wirklich cool!, danke für den Beitrag!
Im letzten JS-Beispiel sollte
abortController.signalnichtcontroller.signalsein?, da es keine Variable namensabortControllergibt.Behoben. Danke.
Ja, es funktioniert nicht.
Wenn man sich die Plattform-Bug-Tickets in https://github.com/whatwg/dom/pull/919 ansieht, sieht es tatsächlich so aus, als hätten Firefox und Safari dies bereits implementiert.
Wird die Event-Handler-Funktion nach dem Aufruf von
controller.abort()vom Garbage Collector übernommen?Wenn nicht, scheint die Verwendung von AbortController die Leistung zu beeinträchtigen.
Es scheint, dass die Funktion in Chrome 88 hinter einem Flag liegt und erst standardmäßig in Chrome 90 aktiviert wird. Gemäß Signalen von anderen Browsern deutet dieses Problem darauf hin, dass die Funktion für die Firefox 86-Version geplant ist.
…und das entsprechende Webkit-Problem wurde behoben, daher bin ich zuversichtlich, dass wir bald gute Unterstützung haben werden!
chromestatus.com ist kein guter Ort, um den Status anderer Anbieter zu verfolgen, da sie ihn nicht automatisch verfolgen und diesen Teil nicht oft aktualisieren…
Signale in Event-Listenern werden in Firefox jetzt unterstützt, aber iOS und Desktop Safari beides noch ohne Unterstützung.
https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener#browser_compatibility
Wir haben heute geprüft, ob dies nun in allen Browsern funktioniert, und ich kann bestätigen, dass Safari 15 und Firefox 101 dies ebenfalls unterstützen.
Wenn Sie Safari 14 unterstützen müssen, können Sie diesen kleinen Polyfill verwenden.
https://github.com/nuxodin/lazyfill/blob/main/monkeyPatches/addEventListenerSignal.js