| [ Web Proxy ] |
| Viewing: https://developer.mozilla.org/de/docs/Web/JavaScript/Guide/Resource_management | [Back] [Original] |
Get to know MDN better
Dieser Inhalt wurde automatisch aus dem Englischen bersetzt, und kann Fehler enthalten. Erfahre mehr ber dieses Experiment.
Dieser Leitfaden behandelt, wie Ressourcenmanagement in JavaScript durchgefhrt wird. Ressourcenmanagement ist nicht genau dasselbe wie Speichermanagement, welches ein fortgeschritteneres Thema ist und blicherweise automatisch von JavaScript gehandhabt wird. Ressourcenmanagement bezieht sich auf das Verwalten von Ressourcen, die nicht automatisch von JavaScript bereinigt werden. Manchmal ist es in Ordnung, einige ungenutzte Objekte im Speicher zu haben, weil sie die Anwendungslogik nicht beeintrchtigen, aber Ressourcenlecks fhren oft dazu, dass Dinge nicht funktionieren oder ein erheblich erhhter Speicherverbrauch auftritt. Daher ist dies kein optionales Feature zur Optimierung, sondern ein Kernmerkmal, um korrekte Programme zu schreiben!
Hinweis:
Auch wenn Speichermanagement und Ressourcenmanagement zwei separate Themen sind, knnen Sie manchmal als letztes Mittel in das Speichermanagement-Hilfssystem einhaken, um Ressourcenmanagement durchzufhren. Wenn Sie beispielsweise ein JavaScript-Objekt haben, das einen Handle einer externen Ressource darstellt, knnen Sie ein FinalizationRegistry erstellen, um die Ressource zu bereinigen, wenn der Handle gesammelt wird, da es definitiv keine Mglichkeit gibt, danach auf die Ressource zuzugreifen. Es gibt jedoch keine Garantie, dass der Finalizer ausgefhrt wird, daher ist es keine gute Idee, sich bei kritischen Ressourcen darauf zu verlassen.
using und await using Deklarationen
DisposableStack und AsyncDisposableStack Objekte
Schauen wir uns zunchst ein paar Beispiele von Ressourcen an, die verwaltet werden mssen:
Datei-Handles: Ein Datei-Handle wird verwendet, um Bytes in einer Datei zu lesen und zu schreiben. Wenn Sie damit fertig sind, mssen Sie fileHandle.close() aufrufen, ansonsten bleibt die Datei geffnet, selbst wenn das JS-Objekt nicht mehr zugnglich ist. Wie in den verlinkten Node.js-Dokumenten steht:
Wenn ein
<FileHandle>nicht mit der MethodefileHandle.close()geschlossen wurde, versucht es, den Dateideskriptor automatisch zu schlieen und eine Warnung im Prozess zu generieren, was hilft, Speicherlecks zu vermeiden. Bitte verlassen Sie sich nicht auf dieses Verhalten, da es unzuverlssig sein kann und die Datei mglicherweise nicht geschlossen wird. Schlieen Sie stattdessen immer explizit<FileHandle>s. Node.js kann dieses Verhalten in Zukunft ndern.
Netzwerkverbindungen: Einige Verbindungen, wie WebSocket und RTCPeerConnection, mssen geschlossen werden, wenn keine Nachrichten bertragen werden. Andernfalls bleibt die Verbindung offen, und Verbindungspools sind oft sehr begrenzt in ihrer Gre.
Stream-Reader: Wenn Sie ReadableStreamDefaultReader.releaseLock() nicht aufrufen, wird der Stream gesperrt und lsst keinen anderen Reader zu, ihn zu konsumieren.
Hier ist ein konkretes Beispiel, das einen lesbaren Stream verwendet:
const stream = new ReadableStream({
start(controller) {
controller.enqueue("a");
controller.enqueue("b");
controller.enqueue("c");
controller.close();
},
});
async function readUntil(stream, text) {
const reader = stream.getReader();
let chunk = await reader.read();
while (!chunk.done && chunk.value !== text) {
console.log(chunk);
chunk = await reader.read();
}
// We forgot to release the lock here
}
readUntil(stream, "b").then(() => {
const anotherReader = stream.getReader();
// TypeError: ReadableStreamDefaultReader constructor can only
// accept readable streams that are not yet locked to a reader
});
Hier haben wir einen Stream, der drei Datensegmente ausgibt. Wir lesen vom Stream, bis wir den Buchstaben "b" finden. Wenn readUntil zurckkehrt, ist der Stream nur teilweise konsumiert, sodass wir in der Lage sein sollten, weiterhin von ihm mit einem anderen Reader zu lesen. Allerdings haben wir vergessen, die Sperre freizugeben, sodass, obwohl reader nicht mehr verfgbar ist, der Stream weiterhin gesperrt ist und wir keinen weiteren Reader erstellen knnen.
Die Lsung in diesem Fall ist einfach: rufen Sie reader.releaseLock() am Ende von readUntil auf. Aber einige Probleme bleiben dennoch:
Inkonsistenz: verschiedene Ressourcen haben unterschiedliche Wege, um sie freizugeben. Zum Beispiel haben wir close(), releaseLock(), disconnect() usw. Das Muster ist nicht verallgemeinerbar.
Fehlerbehandlung: Was passiert, wenn der Aufruf reader.read() fehlschlgt? Dann wrde readUntil terminieren und niemals zum Aufruf reader.releaseLock() gelangen. Wir knnen dies beheben, indem wir try...finally verwenden:
async function readUntil(stream, text) {
const reader = stream.getReader();
try {
let chunk = await reader.read();
while (!chunk.done && chunk.value !== text) {
console.log(chunk);
chunk = await reader.read();
}
} finally {
reader.releaseLock();
}
}
Aber Sie mssen daran denken, dies jedes Mal zu tun, wenn Sie eine wichtige Ressource freigeben mssen.
Geltungsbereich: im obigen Beispiel ist reader bereits geschlossen, wenn wir die try...finally-Anweisung verlassen, aber es ist weiterhin in seinem Geltungsbereich verfgbar. Das bedeutet, dass Sie es mglicherweise versehentlich nach dem Schlieen verwenden.
Mehrere Ressourcen: wenn wir zwei Reader auf verschiedenen Streams haben, mssen wir daran denken, beide freizugeben. Dies ist ein respektabler Versuch, dies zu tun:
const reader1 = stream1.getReader();
const reader2 = stream2.getReader();
try {
// do something with reader1 and reader2
} finally {
reader1.releaseLock();
reader2.releaseLock();
}
Dies fhrt jedoch zu weiteren Problemen bei der Fehlerbehandlung. Wenn stream2.getReader() eine Ausnahme auslst, wird reader1 nicht freigegeben; wenn reader1.releaseLock() einen Fehler auslst, wird reader2 nicht freigegeben. Das bedeutet, dass wir tatschlich jedes Ressourcen-Akquisitions-Freigabe-Paar in seinen eigenen try...finally blockieren mssen:
const reader1 = stream1.getReader();
try {
const reader2 = stream2.getReader();
try {
// do something with reader1 and reader2
} finally {
reader2.releaseLock();
}
} finally {
reader1.releaseLock();
}
Sie sehen, wie eine scheinbar harmlose Aufgabe des Aufrufs von releaseLock schnell zu verschachteltem Boilerplate-Code fhren kann. Aus diesem Grund bietet JavaScript integrierte Sprachuntersttzung fr Ressourcenmanagement.
using und await using DeklarationenDie Lsung, die wir haben, sind zwei spezielle Arten von Variablendeklarationen: using und await using. Sie sind const hnlich, aber sie geben die Ressource automatisch frei, wenn die Variable aus dem Geltungsbereich tritt, sofern die Ressource entsorgbar ist. Mit demselben Beispiel wie oben knnen wir es umschreiben als:
{
using reader1 = stream1.getReader();
using reader2 = stream2.getReader();
// do something with reader1 and reader2
// Before we exit the block, reader1 and reader2 are automatically released
}
Hinweis:
Zum Zeitpunkt des Schreibens implementiert der ReadableStreamDefaultReader nicht das entsorgbare Protokoll. Dies ist ein hypothetisches Beispiel.
Zunchst beachten Sie die zustzlichen geschweiften Klammern um den Code. Dies erstellt einen neuen Block Scope fr die using Deklarationen. Mit using deklarierte Ressourcen werden automatisch freigegeben, wenn sie aus dem Geltungsbereich von using austreten, was in diesem Fall der Fall ist, sobald wir den Block verlassen, sei es, weil alle Anweisungen ausgefhrt wurden oder weil irgendwo ein Fehler oder return/break/continue aufgetreten ist.
Das bedeutet, dass using nur in einem Geltungsbereich verwendet werden kann, der eine klare Lebensdauer hat namentlich kann es nicht auf oberster Ebene eines Skripts verwendet werden, da Variablen auf oberster Ebene eines Skripts im Geltungsbereich fr alle zuknftigen Skripte auf der Seite sind, was praktisch bedeutet, dass die Ressource niemals freigegeben werden kann, wenn die Seite niemals entladen wird. Sie knnen es jedoch auf oberster Ebene eines Moduls verwenden, da der Modul-Geltungsbereich endet, wenn das Modul die Ausfhrung beendet.
Jetzt wissen wir wann using die Reinigung durchfhrt. Aber wie wird es gemacht? using erfordert, dass die Ressource das entsorgbare Protokoll implementiert. Ein Objekt ist entsorgbar, wenn es die Methode [Symbol.dispose]() hat. Diese Methode wird ohne Argumente aufgerufen, um die Reinigung durchzufhren. Zum Beispiel kann im Fall des Readers die [Symbol.dispose]-Eigenschaft ein einfacher Alias oder Wrapper von releaseLock sein:
// For demonstration
class MyReader {
// A wrapper
[Symbol.dispose]() {
this.releaseLock();
}
releaseLock() {
// Logic to release resources
}
}
// OR, an alias
MyReader.prototype[Symbol.dispose] = MyReader.prototype.releaseLock;
Durch das entsorgbare Protokoll kann using alle Ressourcen konsistent entsorgen, ohne zu verstehen, um welchen Ressourcentyp es sich handelt.
Jeder Geltungsbereich hat eine Liste von Ressourcen, die ihm in der Reihenfolge, in der sie deklariert wurden, zugeordnet sind. Wenn der Geltungsbereich endet, werden die Ressourcen in umgekehrter Reihenfolge entsorgt, indem ihre [Symbol.dispose]()-Methode aufgerufen wird. Zum Beispiel wird im obigen Beispiel reader1 vor reader2 deklariert, sodass reader2 zuerst entsorgt wird, dann reader1. Fehler, die beim Versuch auftreten, eine Ressource zu entsorgen, verhindern nicht die Entsorgung anderer Ressourcen. Dies ist konsistent mit dem try...finally Muster und respektiert mgliche Abhngigkeiten zwischen Ressourcen.
await using ist using sehr hnlich. Die Syntax sagt Ihnen, dass irgendwo ein await passiert nicht, wenn die Ressource deklariert wird, sondern tatschlich, wenn sie entsorgt wird. await using erfordert, dass die Ressource asynchron entsorgbar ist, was bedeutet, dass sie eine Methode [Symbol.asyncDispose]() hat. Diese Methode wird ohne Argumente aufgerufen und gibt ein Versprechen zurck, das aufgelst wird, wenn die Reinigung abgeschlossen ist. Dies ist ntzlich, wenn die Reinigung asynchron ist, wie fileHandle.close(), in welchem Fall das Ergebnis der Entsorgung nur asynchron bekannt sein kann.
{
await using fileHandle = open("file.txt", "w");
await fileHandle.write("Hello");
// fileHandle.close() is called and awaited
}
Da await using erfordert, dass ein await ausgefhrt wird, ist es nur in Kontexten erlaubt, in denen await erlaubt ist, zu denen async Funktionen und oberstes await in Modulen gehren.
Ressourcen werden sequentiell, nicht gleichzeitig, bereinigt: Der Rckgabewert der [Symbol.asyncDispose]()-Methode einer Ressource wird awaited, bevor die [Symbol.asyncDispose]()-Methode der nchsten Ressource aufgerufen wird.
Einige Punkte, die zu beachten sind:
using und await using sind opt-in. Wenn Sie Ihre Ressource mit let, const oder var deklarieren, erfolgt keine automatische Entsorgung, genau wie bei anderen nicht entsorgbaren Werten.using und await using erfordern, dass die Ressource entsorgbar (oder asynchron entsorgbar) ist. Wenn die Ressource nicht die Methode [Symbol.dispose]() oder [Symbol.asyncDispose]() hat, erhalten Sie einen TypeError an der Deklarationszeile. Die Ressource kann jedoch null oder undefined sein, sodass Sie Ressourcen bedingt erwerben knnen.const knnen using und await using Variablen nicht neu zugewiesen werden, obwohl die Eigenschaften der von ihnen gehaltenen Objekte gendert werden knnen. Die Methode [Symbol.dispose]()/[Symbol.asyncDispose]() wird jedoch zum Zeitpunkt der Deklaration bereits gespeichert, sodass eine nderung der Methode nach der Deklaration die Bereinigung nicht beeintrchtigt.using fr einige Beispiele.DisposableStack und AsyncDisposableStack Objekteusing und await using sind spezielle Syntaxen. Syntaxen sind praktisch und verbergen viel von der Komplexitt, aber manchmal mssen Sie Dinge manuell erledigen.
Fr ein hufiges Beispiel: Was, wenn Sie die Ressource nicht am Ende dieses Geltungsbereichs, sondern in einem spteren Geltungsbereich entsorgen mchten? Betrachten Sie dies:
let reader;
if (someCondition) {
reader = stream.getReader();
} else {
reader = stream.getReader({ mode: "byob" });
}
Wie gesagt, using ist wie const: es muss initialisiert werden und kann nicht neu zugewiesen werden, sodass Sie dies versuchen knnten:
if (someCondition) {
using reader = stream.getReader();
} else {
using reader = stream.getReader({ mode: "byob" });
}
Dies bedeutet jedoch, dass alle Logik innerhalb des if oder else geschrieben werden muss, was zu viel Duplizierung fhrt. Was wir tun mchten, ist, die Ressource in einem Geltungsbereich zu erwerben und zu registrieren, aber sie in einem anderen zu entsorgen. Dafr knnen wir einen DisposableStack verwenden, welcher ein Objekt ist, das eine Sammlung von entsorgbaren Ressourcen hlt und selbst entsorgbar ist:
{
using disposer = new DisposableStack();
let reader;
if (someCondition) {
reader = disposer.use(stream.getReader());
} else {
reader = disposer.use(stream.getReader({ mode: "byob" }));
}
// Do something with reader
// Before scope exit, disposer is disposed, which disposes reader
}
Mglicherweise haben Sie eine Ressource, die das entsorgbare Protokoll noch nicht implementiert, sodass sie von using abgelehnt wird. In diesem Fall knnen Sie adopt() verwenden.
{
using disposer = new DisposableStack();
// Suppose reader does not have the [Symbol.dispose]() method,
// then it cannot be used with using.
// However, we can manually pass a disposer function to disposer.adopt
const reader = disposer.adopt(stream.getReader(), (reader) =>
reader.releaseLock(),
);
// Do something with reader
// Before scope exit, disposer is disposed, which disposes reader
}
Vielleicht haben Sie eine Entsorgungsaktion durchzufhren, die nicht mit einer bestimmten Ressource verbunden ist. Vielleicht mchten Sie einfach eine Nachricht mit "Alle Datenbankverbindungen geschlossen" protokollieren, wenn mehrere Verbindungen gleichzeitig geffnet sind. In diesem Fall knnen Sie defer() verwenden.
{
using disposer = new DisposableStack();
disposer.defer(() => console.log("All database connections closed"));
const connection1 = disposer.use(openConnection());
const connection2 = disposer.use(openConnection());
// Do something with connection1 and connection2
// Before scope exit, disposer is disposed, which first disposes connection1
// and connection2 and then logs the message
}
Sie mchten mglicherweise eine bedingte Entsorgung durchfhren zum Beispiel nur beanspruchte Ressourcen entsorgen, wenn ein Fehler aufgetreten ist. In diesem Fall knnen Sie move() verwenden, um die Ressourcen zu erhalten, die sonst entsorgt wrden.
class MyResource {
#resource1;
#resource2;
#disposables;
constructor() {
using disposer = new DisposableStack();
this.#resource1 = disposer.use(getResource1());
this.#resource2 = disposer.use(getResource2());
// If we made it here, then there were no errors during construction and
// we can safely move the disposables out of `disposer` and into `#disposables`.
this.#disposables = disposer.move();
// If construction failed, then `disposer` would be disposed before reaching
// the line above, disposing `#resource1` and `#resource2`.
}
[Symbol.dispose]() {
this.#disposables.dispose(); // Dispose `#resource2` and `#resource1`.
}
}
AsyncDisposableStack ist hnlich wie DisposableStack, aber fr die Verwendung mit asynchron entsorgbaren Ressourcen. Seine use()-Methode erwartet ein asynchron entsorgbares Objekt, seine adopt()-Methode erwartet eine asynchrone Bereinigungsfunktion, und seine dispose()-Methode erwartet eine asynchrone Callback-Funktion. Es bietet eine [Symbol.asyncDispose]()-Methode. Sie knnen ihm weiterhin synchrone Ressourcen bergeben, wenn Sie eine Mischung aus synchronen und asynchronen haben.
Die Referenz zu DisposableStack enthlt weitere Beispiele und Details.
Ein wesentlicher Anwendungsfall der Ressourcenmanagement Funktion ist sicherzustellen, dass Ressourcen immer entsorgt werden, auch wenn ein Fehler auftritt. Lassen Sie uns einige komplexe Fehlerbehandlungsszenarien untersuchen.
Wir starten mit dem folgenden Code, der durch die Verwendung von using robust gegen Fehler ist:
async function readUntil(stream, text) {
// Use `using` instead of `await using` because `releaseLock` is synchronous
using reader = stream.getReader();
let chunk = await reader.read();
while (!chunk.done && chunk.value !== text) {
console.log(chunk.toUpperCase());
chunk = await reader.read();
}
}
Nehmen wir an, dass chunk sich als null herausstellt. Dann wird !chunk.done einen TypeError auslsen, was dazu fhrt, dass die Funktion beendet wird. Bevor die Funktion beendet wird, wird stream[Symbol.dispose]() aufgerufen, was die Sperre des Streams freigibt.
const stream = new ReadableStream({
start(controller) {
controller.enqueue("a");
controller.enqueue(null);
controller.enqueue("b");
controller.enqueue("c");
controller.close();
},
});
readUntil(stream, "b")
.catch((e) => console.error(e)) // TypeError: chunk.toUpperCase is not a function
.then(() => {
const anotherReader = stream.getReader();
// Successfully creates another reader
});
Daher schluckt using keine Fehler: Alle auftretenden Fehler werden weiterhin ausgelst, aber die Ressourcen werden kurz davor geschlossen. Was passiert nun, wenn die Ressourcenbereinigung selbst auch einen Fehler auslst? Lassen Sie uns ein konstruiertes Beispiel verwenden:
class MyReader {
[Symbol.dispose]() {
throw new Error("Failed to release lock");
}
}
function doSomething() {
using reader = new MyReader();
throw new Error("Failed to read");
}
try {
doSomething();
} catch (e) {
console.error(e); // SuppressedError: An error was suppressed during disposal
}
Es gibt zwei Fehler, die im Aufruf von doSomething() generiert werden: einen Fehler, der whrend doSomething geworfen wird, und einen Fehler, der whrend der Entsorgung von reader aufgrund des ersten Fehlers geworfen wird. Beide Fehler werden zusammen geworfen, sodass das, was Sie fangen, ein SuppressedError ist. Dies ist ein spezieller Fehler, der zwei Fehler umschliet: die Fehler-Eigenschaft enthlt den spteren Fehler, und die unterdrckte-Eigenschaft enthlt den frheren Fehler, der durch den spteren Fehler "unterdrckt" wird.
Wenn wir mehr als eine Ressource haben und beide einen Fehler whrend der Entsorgung auslsen (dies sollte uerst selten sein es ist schon selten, dass die Entsorgung fehlschlgt!), dann wird jeder frhere Fehler durch den spteren Fehler unterdrckt, wodurch eine Kette von unterdrckten Fehlern entsteht.
class MyReader {
[Symbol.dispose]() {
throw new Error("Failed to release lock on reader");
}
}
class MyWriter {
[Symbol.dispose]() {
throw new Error("Failed to release lock on writer");
}
}
function doSomething() {
using reader = new MyReader();
using writer = new MyWriter();
throw new Error("Failed to read");
}
try {
doSomething();
} catch (e) {
console.error(e); // SuppressedError: An error was suppressed during disposal
console.error(e.suppressed); // SuppressedError: An error was suppressed during disposal
console.error(e.error); // Error: Failed to release lock on reader
console.error(e.suppressed.suppressed); // Error: Failed to read
console.error(e.suppressed.error); // Error: Failed to release lock on writer
}
reader wird zuletzt freigegeben, daher ist sein Fehler der neueste und unterdrckt daher alles andere: er wird als e.error angezeigt.writer wird zuerst freigegeben, daher ist sein Fehler spter als der ursprngliche Austrittsfehler, aber frher als der reader-Fehler: er wird als e.suppressed.error angezeigt.e.suppressed.suppressed angezeigt wird.Im folgenden Beispiel erstellen wir eine Objekt-URL zu einem Blob (in einer realen Anwendung wrde dieser Blob von irgendwoher abgerufen werden, z.B. aus einer Datei oder einer Fetch-Antwort), sodass wir den Blob als Datei herunterladen knnen. Um ein Ressourcenleck zu vermeiden, mssen wir die Objekt-URL mit URL.revokeObjectURL() freigeben, wenn sie nicht mehr bentigt wird (das heit, wenn der Download erfolgreich gestartet wurde). Da die URL selbst lediglich ein String ist und daher nicht das entsorgbare Protokoll implementiert, knnen wir url nicht direkt mit using deklarieren; stattdessen erstellen wir einen DisposableStack, der als Entsorger fr url dient. Die Objekt-URL wird freigegeben, sobald disposer den Geltungsbereich verlsst, was entweder geschieht, wenn link.click() endet oder irgendwo ein Fehler auftritt.
const downloadButton = document.getElementById("download-button");
const exampleBlob = new Blob(["example data"]);
downloadButton.addEventListener("click", () => {
using disposer = new DisposableStack();
const link = document.createElement("a");
const url = disposer.adopt(
URL.createObjectURL(exampleBlob),
URL.revokeObjectURL,
);
link.href = url;
link.download = "example.txt";
link.click();
});
Im folgenden Beispiel rufen wir eine Liste von Ressourcen gleichzeitig mit Promise.all() ab. Promise.all() schlgt fehl und lehnt das resultierende Versprechen ab, sobald eine Anfrage fehlgeschlagen ist; jedoch laufen die anderen noch ausstehenden Anfragen weiter, obwohl ihre Ergebnisse im Programm nicht zugnglich sind. Um zu verhindern, dass diese verbleibenden Anfragen unntig Ressourcen verbrauchen, mssen wir sicherstellen, dass ausstehende Anfragen automatisch abgebrochen werden, wenn Promise.all() abschliet. Wir implementieren den Abbruch mit einem AbortController und bergeben dessen signal an jeden fetch() Aufruf. Wenn Promise.all() erfllt wird, kehrt die Funktion normal zurck und der Controller bricht ab, was harmlos ist, da keine ausstehende Anfrage abzubrechen ist; wenn Promise.all() abgelehnt wird und die Funktion einen Fehler wirft, bricht der Controller ab und alle ausstehenden Anfragen werden abgebrochen.
async function getAllData(urls) {
using disposer = new DisposableStack();
const { signal } = disposer.adopt(new AbortController(), (controller) =>
controller.abort(),
);
// Fetch all URLs in parallel
// Automatically cancel any incomplete requests if any request fails
const pages = await Promise.all(
urls.map((url) =>
fetch(url, { signal }).then((response) => {
if (!response.ok)
throw new Error(
`Response error: ${response.status} - ${response.statusText}`,
);
return response.text();
}),
),
);
return pages;
}
Die Ressourcenentsorgungssyntax bietet viele starke Fehlerbehandlungsgarantien, die sicherstellen, dass die Ressourcen immer bereinigt werden, egal was passiert, aber es gibt einige Stolperfallen, auf die Sie mglicherweise stoen:
using oder await using. Die Ressourcenmanagement-Syntax ist nur da, um Ihnen zu helfen, wenn Sie wissen, dass Sie sie bentigen, aber es gibt nichts, das Ihnen sagt, wenn Sie sie vergessen, zu verwenden! Leider gibt es keinen guten Weg, dies vor der Tat zu verhindern, da es keine syntaktischen Hinweise gibt, dass etwas eine entsorgbare Ressource ist, und selbst bei entsorgbaren Ressourcen mchten Sie mglicherweise, sie ohne automatische Entsorgung deklarieren. Wahrscheinlich bentigen Sie einen Typprfer in Kombination mit einem Linter, um diese Probleme zu erkennen, wie typescript-eslint (das immer noch plant, an diesem Feature zu arbeiten).using-Syntax sicher, dass eine Ressource freigegeben wird, wenn sie den Geltungsbereich verlsst, aber es gibt viele Mglichkeiten, einen Wert ber seine bindende Variable hinaus zu erhalten. JavaScript hat keinen Besitzmechanismus wie Rust, daher knnen Sie ein Alias deklarieren, das using nicht verwendet, oder die Ressource in einer closure bewahren, usw. Die using Referenz enthlt viele Beispiele fr solche Stolperfallen. Auch hier gibt es keinen guten Weg, dies in einem komplizierten Kontrollfluss richtig zu erkennen, daher mssen Sie vorsichtig sein.Die Ressourcenmanagement-Funktion ist kein Allheilmittel. Sie ist definitiv eine Verbesserung gegenber dem manuellen Aufrufen der Entsorgungsmethoden, aber sie ist nicht intelligent genug, um alle Ressourcenmanagementfehler zu verhindern. Sie mssen immer noch vorsichtig sein und die Semantik der Ressourcen, die Sie verwenden, verstehen.
Hier sind die Schlsselelemente des Ressourcenmanagementsystems:
using und await using Deklarationen fr automatische Ressourcentsorgung.Symbol.dispose und Symbol.asyncDispose verwenden, die von den Ressourcen implementiert werden sollen.DisposableStack und AsyncDisposableStack Objekte fr Flle, in denen using und await using nicht geeignet sind.Mit der ordnungsgemen Verwendung dieser APIs knnen Sie Systeme erstellen, die mit externen Ressourcen interagieren und stark und robust gegenber allen Fehlerbedingungen bleiben, ohne viel Boilerplate-Code.
Der Bauplan fr ein besseres Internet.
Teile dieses Inhalts sind 19982026 von einzelnen mozilla.org-Mitwirkenden. Inhalte sind verfgbar unter einer Creative-Commons-Lizenz.
| Web Proxy Viewer | New URL | Original Page |