using

Deklaracja zasobu, który zostanie automatycznie zwolniony przy wyjściu z bloku, nawet po błędzie.

Na tej stronie

Przykład

#
TypeScript
// Polyfill: potrzebny tylko tam, gdzie brakuje wbudowanego Symbol.dispose
(Symbol as { dispose?: symbol }).dispose ??= Symbol('Symbol.dispose')

class DbConnection implements Disposable {
  constructor(private name: string) {
    console.log(`Otwieram ${name}`)
  }

  query(sql: string) {
    console.log(`${this.name}: ${sql}`)
  }

  [Symbol.dispose]() {
    console.log(`Zamykam ${this.name}`)
  }
}

function saveProgress() {
  using db = new DbConnection('progress-db')
  db.query('UPDATE progress SET exp = exp + 20')
  console.log('Koniec funkcji')
}

saveProgress()
Wynik
Otwieram progress-db
progress-db: UPDATE progress SET exp = exp + 20
Koniec funkcji
Zamykam progress-db

Definicja i zastosowanie

#

Deklaracja using działa jak const, ale dodatkowo sprząta po sobie. Gdy program opuszcza blok z taką deklaracją, niezależnie od tego, czy dotrze do jego końca, wyjdzie przez return, czy przez wyjątek, wywoływana jest metoda [Symbol.dispose]() przechowywanego obiektu. W ten sposób zamkniesz połączenie z bazą, plik albo blokadę bez ręcznego pisania try...finally.

Obiekt nadaje się do using, jeśli ma metodę [Symbol.dispose](), czyli spełnia interfejs Disposable. Wartości null i undefined są dozwolone i zostają pominięte. Kilka zasobów w jednym bloku zwalnianych jest w kolejności odwrotnej do utworzenia. Wersja await using czeka na asynchroniczne sprzątanie w metodzie [Symbol.asyncDispose]() i działa tam, gdzie wolno użyć await.

TypeScript obsługuje tę składnię od wersji 5.2. W lib potrzebne są typy esnext.disposable (albo całe esnext). Przy target niższym niż ESNext kompilator zamienia using na kod pomocniczy, który korzysta z Symbol.dispose. Node.js 22 ma ten symbol wbudowany, ale nie każde środowisko go zna, dlatego pierwszy przykład zaczyna się od jednolinijkowego polyfilla.

Składnia

#
Składnia
using zasob = utworzZasob()
await using polaczenie = await polacz()

class Zasob implements Disposable {
  [Symbol.dispose]() { … }
}

Więcej przykładów

#
Kilka zasobów i wyjątek
TypeScript
function lock(name: string): Disposable {
  console.log(`+ ${name}`)
  return { [Symbol.dispose]: () => console.log(`- ${name}`) }
}

function gradeExam() {
  using sheet = lock('arkusz')
  using results = lock('wyniki')
  throw new Error('brak odpowiedzi')
}

try {
  gradeExam()
} catch (error) {
  console.log((error as Error).message)
}
Wynik
+ arkusz
+ wyniki
- wyniki
- arkusz
brak odpowiedzi
await using
TypeScript
// await using korzysta z Symbol.asyncDispose, więc i on dostaje polyfill
(Symbol as { asyncDispose?: symbol }).asyncDispose ??= Symbol('Symbol.asyncDispose')

async function connect(name: string): Promise<AsyncDisposable> {
  console.log(`Łączę z ${name}`)
  return {
    async [Symbol.asyncDispose]() {
      console.log(`Rozłączam ${name}`)
    }
  }
}

async function syncProgress() {
  await using api = await connect('progress-api')
  console.log('Wysyłam postęp')
}

await syncProgress()
console.log('Gotowe')
Wynik
Łączę z progress-api
Wysyłam postęp
Rozłączam progress-api
Gotowe

Dobre praktyki

#
  • Błąd „Symbol.dispose is not defined” w działającym programie oznacza, że środowisko nie zna jeszcze tego symbolu. Polyfill z pierwszego przykładu umieść na samym początku programu, przed definicjami klas, które go używają.
  • Zmiennej z using nie można nadpisać. Próba przypisania kończy się błędem „Cannot assign to 'db' because it is a constant”.
  • Jeśli zasób jest potrzebny tylko przez chwilę, otwórz dla niego osobny blok { … }. Zostanie zwolniony przy klamrze zamykającej, a nie dopiero na końcu funkcji.

Powiązane hasła

#

Widzisz błąd albo brakuje przykładu? Napisz do nas.