کلمه کلیدی using در TypeScript و مدیریت صریح منابع (Explicit Resource Management)

نشت حافظه (Memory leaks) در محیط عملیاتی اغلب از یک خطا ناشی می‌شود: توسعه‌دهندگان منابع را دریافت می‌کنند اما در آزادسازی آن‌ها شکست می‌خورند.

اتصالات پایگاه داده پس از بروز خطا باز می‌مانند. هندل‌های فایل (File handles) منابع سیستم را مصرف می‌کنند. کلاینت‌های WebSocket به سرورهای قطع شده متصل می‌مانند. این اتفاق زمانی رخ می‌دهد که شما برای پاک‌سازی به بلوک‌های finally دستی تکیه می‌کنید.

کلمه کلیدی using در TypeScript این مشکل را حل می‌کند. این قابلیت بخشی از پیشنهاد مدیریت صریح منابع (Explicit Resource Management) در ECMAScript است. این ویژگی، پاک‌سازی را از طریق الگوی قابل‌دفع (disposable pattern) تضمین می‌کند. وقتی یک منبع از محدوده (scope) خود خارج می‌شود، TypeScript متد آزادسازی (disposal method) آن را به‌طور خودکار اجرا می‌کند.

دیگر نیازی به بلوک‌های finally دستی ندارید. دیگر پاک‌سازی را فراموش نمی‌کنید. دیگر باعث نشت اتصالات نمی‌شوید.

نحوه عملکرد:

  • کلمه کلیدی using تضمین می‌کند که هنگام خروج منابع از یک محدوده (scope)، آزادسازی انجام شود.
  • منابع قابل‌دفع (Disposable resources) برای وظایف همگام (sync) از Symbol.dispose و برای وظایف ناهمگام (async) از Symbol.asyncDispose استفاده می‌کنند.
  • TypeScript اعلان‌های using را به بلوک‌های try-finally همراه با یک پشته آزادسازی (disposal stack) خودکار تبدیل می‌کند.
  • این الگو از نشت‌های ناشی از بازگشت‌های زودهنگام (early returns)، استثناهای پرتاب شده (thrown exceptions) یا کدهای فراموش شده جلوگیری می‌کند.

از using برای هر منبعی با طول عمر مشخص، مانند اتصالات پایگاه داده، هندل‌های فایل، قفل‌ها (locks) یا تایمرها استفاده کنید.

این مکانیسم از یک پروتکل آزادسازی استفاده می‌کند. اشیاء متدی را پیاده‌سازی می‌کنند که با کلید Symbol.dispose مشخص شده است. وقتی محدوده از طریق اتمام عادی، یک دستور return یا یک استثنا (exception) خارج می‌شود، TypeScript آن متد را فراخوانی می‌کند.

مثالی از یک هندل فایل:

class FileHandle {
  private handle: number;

  constructor(path: string) {
    this.handle = openFileSync(path);
  }

  [Symbol.dispose]() {
    if (this.handle !== -1) {
      closeFileSync(this.handle);
      this.handle = -1;
    }
  }

  read(buffer: Buffer): number {
    return readSync(this.handle, buffer);
  }
}

function processFile(path: string) {
  using file = new FileHandle(path);
  const buffer = Buffer.alloc(1024);
  file.read(buffer);
}

فرآیند آزادسازی در یک بلوک finally تولید شده اجرا می‌شود. این فرآیند حتی اگر تابع خطایی پرتاب کند یا زودتر از موعد بازگردد، اجرا می‌شود.

TypeScript یک پشته آزادسازی را مدیریت می‌کند. منابع را به ترتیب معکوسِ نحوه دریافت آن‌ها آزاد می‌کند. این کار با ترتیب وابستگی طبیعی در اکثر منطق‌های پاک‌سازی مطابقت دارد.

برای پاک‌سازی ناهمگام (asynchronous)، از await using استفاده کنید. این کار مستلزم آن است که منبع، Symbol.asyncDispose را پیاده‌سازی کند. TypeScript قبل از ادامه کار، منتظر (await) Promise بازگشتی می‌ماند.

سه قانون برای موفقیت:

  • داخل متدهای آزادسازی خود استثنا (exception) پرتاب نکنید. در عوض، خطاها را در داخل متد مدیریت (catch) و ثبت (log) کنید.
  • از عملیات ناهمگام (async) در یک متد همگامِ Symbol.dispose استفاده نکنید. در عوض از Symbol.asyncDispose استفاده کنید.
  • به یاد داشته باشید که اعلان‌های using دارای محدوده بلوکی (block-scoped) هستند. منبعی که داخل یک بلوک if قرار دارد، زمانی که آن بلوک تمام شود آزاد می‌شود، نه زمانی که تابع تمام شود.

این الگو امنیت را با هزینه زمان اجرای (runtime cost) ناچیز فراهم می‌کند.

منبع: https://dev.to/jsmanifest/typescript-using-keyword-and-explicit-resource-management-done-right-22pg