TypeScriptの using キーワードと明示的なリソース管理 (Explicit Resource Management)

本番環境でのメモリリークは、多くの場合、ある一つのミスに起因します。それは、開発者がリソースを取得したものの、それを解放し忘れることです。

エラーが発生した後もデータベース接続が開いたままになったり、ファイルハンドルがシステムリソースを消費し続けたり、WebSocketクライアントが切断されたサーバーに接続されたままになったりします。これは、finally ブロック内での手動のクリーンアップに依存している場合に発生します。

TypeScriptの using キーワードはこの問題を解決します。これは ECMAScript の Explicit Resource Management プロポーザルの一部です。このキーワードは、Disposable パターンを通じてクリーンアップを保証します。リソースがそのスコープを抜けると、TypeScript はその破棄(disposal)メソッドを自動的に実行します。

もはや手動の finally ブロックは必要ありません。クリーンアップを忘れることも、接続をリークさせることもなくなります。

仕組み:

  • using キーワードは、リソースがスコープを抜ける際に破棄を保証します。
  • Disposable なリソースは、同期タスクには Symbol.dispose を、非同期タスクには Symbol.asyncDispose を実装します。
  • TypeScript は using 宣言を、自動的な破棄スタックを持つ try-finally ブロックに変換します。
  • このパターンにより、早期リターン、例外の送出、あるいはコードの書き忘れによって引き起こされるリークを防ぐことができます。

データベース接続、ファイルハンドル、ロック、タイマーなど、寿命が決まっているあらゆるリソースに using を使用してください。

このメカニズムは破棄プロトコル(disposal protocol)を使用します。オブジェクトは Symbol.dispose をキーとするメソッドを実装します。通常の完了、return、または例外によってスコープを抜ける際、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 は破棄スタックを保持します。リソースは取得した時とは逆の順序で破棄されます。これは、ほとんどのクリーンアップロジックにおける自然な依存関係の順序と一致します。

非同期のクリーンアップには await using を使用します。これには、リソースが Symbol.asyncDispose を実装している必要があります。TypeScript は処理を続行する前に、返された Promise を await します。

成功させるための3つのルール:

  • 破棄メソッド内で例外をスローしないでください。代わりに、内部でエラーをキャッチしてログに記録してください。
  • 同期的な Symbol.dispose メソッド内で非同期操作を行わないでください。代わりに Symbol.asyncDispose を使用してください。
  • using 宣言はブロックスコープであることを忘れないでください。if ブロック内のリソースは、関数が終了した時ではなく、そのブロックが終了した時に破棄されます。

このパターンは、実行時のコストをほとんどかけずに安全性を提供します。

Source: https://dev.to/jsmanifest/typescript-using-keyword-and-explicit-resource-management-done-right-22pg