try-using文を用いるJavaScriptの超モダンな“リソース管理”

:::note alert
この記事で紹介しているExplicit Resource Managementでは、もはやtry-using文は無くなりました。この記事の内容は最新の情報ではありませんのでご注意ください。

最新のプロポーザルでは、using const文やusing await const文が使われることになっています。（2021年10月）
:::

 リソース管理というのはプログラミングにおける頻出課題のひとつです。そもそもリソースとは何かというのは人によって思い浮かべるものが違うかもしれませんが、ここでいうリソースは「**使ったらちゃんと後始末（解放）をしないといけないもの**」だと思ってください。今時はピンと来ない方もいるかもしれませんが、「ファイルを`open`したらちゃんと`close`する」とかおおよそそういう話です。

このようなリソースは、一度使おうものならその後何が起ころうとも必ず後始末をしないといけません。たとえ使っている途中でエラーが起こったとしても、適切なエラーハンドリングを行なって忘れずにリソースの後始末をする責任があります。リソース管理を誤ると、メモリリーク等の原因になりかねません。

この記事では、このような「**リソース管理**」を補助してくれるJavaScriptの**新しい言語機能**を紹介します。今回紹介する[ECMAScript Explicit Resource Management](https://github.com/tc39/proposal-explicit-resource-management)は、つい先日**Stage 2**になったばかりの**プロポーザル**です。Stageとは何かみたいな話はこの記事では語りませんが、ざっくり言うと**方向性は定まってきた**という感じです。もちろん、まだ仕様策定途中であり確定したものではなく、当然ながらブラウザやnode.js等には実装されておらず今試すことはできません。記事タイトルの「超モダン」というのは新しすぎて今はまだ利用不可という意味なのであしからずご了承ください。1〜2年もすれば最新鋭の言語機能になっているかもしれませんから、期待して待ちましょう。

なお、知っている方向けに一言でいうとこれは**C#の`using`文**や**Pythonの`with`文**, そして**Javaの`try-with-resources`**文に相当する言語機能です。機能的にはJavaが一番近いですね。

## `try-using`文を用いるリソース管理の概要

早速中身に入っていきましょう。今回紹介する機能はだいたいこんな感じの構文です。

```js
try using (const resource = makeResource()) {
  // この中ではresourceが使用可能
  resource.write(/* ... */);
}
// try文を出ると同時にresourceが解放される
```

見ると分かるように`try using (...) { ... }`という形の**`try-using`文**が追加されています。後でより詳しい説明がありますが、このように`()`の中で変数宣言をすることができ、`try-using`文のブロックの中でこの変数を使用可能です。

この変数に入っているものが**リソース**であり、`try`文から出るときに自動的にこのリソースに対して“後始末”が行なわれます。後始末というのは具体的にはリソースが持つ`[Symbol.dispose]`メソッドの呼び出しです。

## 新しい構文の詳細

上では一例を紹介しましたが、このプロポーザルで追加される`try-using`構文には主に2種類があります。

一つは上で紹介したように`()`の中で変数を宣言する構文です。この構文では、**`const`による変数宣言のみ可能です**。すなわち、`var`や`let`による変数宣言は構文エラーとなりできません[^reassignment]。

[^reassignment]: ただし、これらのキーワードを使わずに既存の変数に`=`を用いて再代入する場合は、この代入文が式と見なせるため、後述のもうひとつの構文（`()`の中に式を書くもの）に当てはまるため可能となります。すごく紛らわしいので注意しましょう。

ここで宣言された変数は`try-using`文の`{ ... }`の中でのみ使用可能です。そして先ほどの述べたとおり、ブロックから出るときに`[Symbol.dispose]`メソッドの呼び出しによってリソースが解放されます。

```js
try using (const resource = makeResource()) {
  // この中ではresourceが使用可能
  resource.write(/* ... */);
  // ブロックの終わりで resource[Symbol.dispose]() が呼び出される
}
```

もう一種類の構文は、変数宣言を行わずに`()`の中に何らかの式を書くものです。

```js
try using (何らかの式) {
  // ...
}
```

この場合も`{ ... }`の終了時に`何らかの式`の結果得られたオブジェクトに対して`[Symbol.dispose]`メソッドが呼び出されます。つまり、この構文は以下と同じということになります。

```js
try using (const _unused = 何らかの式) {
  // ...
}
```

すなわち、変数に入れてもいいけど別に使わないという場合の糖衣構文ということになります。こちらの構文は、主に「`try-using`文に入ると同時にリソースを作成するのではなく、既存のリソースを`try-using`文に当てはめたい」という場合に有用でしょう。

### エラー発生時の挙動

最初に述べた通り、リソース管理において重要なことは「いかなる場合でも確実にリソースの後始末を行う」ことです。この`try-using`文はそのための機能を持っています。

一言で言うと、「`try-using`文のブロックから途中で脱出する場合でも脱出直前にリソースの`[Symbol.dispose]`メソッドが呼ばれる」という挙動になります。

```js
try using (const resource = makeResource()) {
  // ブロックの中でエラー発生！！！！！！
  throw new Error("OMG");
  // ↓当然これは実行されない
  console.log("Hi");
}
```

この例の場合、この`try-using`文は`catch`（後述）を持たないので、中で発生したエラーは普通に上に伝播していきます。ただ、エラーがこの`try-using`文から脱出するタイミングでしっかりと`resource[Symbol.dispose]()`が呼ばれることになります。

より具体的な例を示すとこんな感じです。今回のリソースは解放されると`console.log`するリソースにしましょう。

```js
function makeResource() {
  return {
    [Symbol.dispose]() {
      console.log("disposed");
    }
  }
}

// エラーをキャッチするための外側のtry文
try {
  console.log("1");
  try using (const resource = makeResource()) {
    console.log("2");
    throw new Error("OMG");
    console.log("ここは通らない");
  }
} catch (err) {
  console.log("3");
}
console.log("4");
```

この例を実行すると`1`→`2`→`3`→`4`の順にログが表示されることは言うまでもありません。では、`resource`が解放されたことを示す`disposed`はどのタイミングで表示されるでしょうか。

答えは`2`と`3`の間です。`2`の後にエラーが投げられますが、それが内側の`try-using`文を出る瞬間に`resource[Symbol.dispose]()`が呼ばれます。その後エラーが外側の`try`文にキャッチされることになります。

また、この例のようにエラーが投げられた場合に限らず、どのような理由で`try-using`文を脱出する場合であっても`[Symbol.dispose]`は呼ばれます。ブロックの中から`return`や`continue`などで脱出する場合なども例外ではありません。そのような挙動は既存の`try-finally`文でも可能ですが、今回の新構文はそれをより便利に行えるものとなっています。


### `catch`や`finally`との組み合わせ

この記事で紹介している`try-using`文は、実は既存の`try`文と同様に**`catch`や`finally`と組み合わせることができます**。もちろん、全部ではなく`try using`と`catch`のみ、あるいは`try using`と`finally`のみということも可能です。全部使う場合はこんな感じの構文になります。

```js
try using (const resource = makeResource()) {
  // 1. resource を使う処理
} catch (error) {
  // 2. エラー処理
} finally {
  // 3. 最後の処理
}
```

お察しの通り、この新しい`try-using`文もやはりエラーをキャッチできる機能を持っています。これまでの例では`catch`が無いので内部で発生したエラーは普通に上に伝播していきましたが、`catch`文がある場合は内部（上の例の`1`の処理）で発生したエラーをキャッチすることができます（`2`の処理）。

この構文は、従来のエラーキャッチ機能に加えてさらにリソース解放機能も併せ持っています。リソースが解放されるタイミングは**`1`の処理の直後**となります。ポイントは、`catch`や`finally`に入るよりも前に解放されるということです。

処理の流れを確認してみましょう。`1`でエラーが発生しなかった場合は以下の挙動となります。

- `1`の処理を実行する。
- `resource[Symbol.dispose]()`を呼ぶ。
- `3`の処理を実行する。

一方、`1`でエラーが発生した場合は以下の挙動となります。

- `1`の処理を実行する（エラーで中断）。
- `resource[Symbol.dispose]()`を呼ぶ。
- `2`の処理を実行する。
- `3`の処理を実行する。

`2`や`3`の中ではリソースが使えないという点は間違いやすいので注意が必要です。`try-using`文で`const`宣言した変数が`1`の中でしか使えないと言う事実がこれとうまく噛み合っていますね。

### 分割代入

`try-using`文の初期化で`const`を用いる場合、`const`における分割代入もサポートされています。具体的にはこういう感じの用法です。

```js
try using (const {read, write} = makeResource()) {
  // ...
}
```

ただ、一つたいへん注意しなければいけないのは、**この場合何に対して`[Symbol.dispose]`が呼ばれるのか**ということです。実は、**分割代入によって作られた変数ひとつひとつに対して`[Symbol.dispose]`が呼ばれます**。

ということは、このブロックから脱出した際には`read[Symbol.dispose]()`と`write[Symbol.dispose]()`が呼ばれるということです。

さらに言えば、下のように書くのとは意味が違うということです。下のコードではリソースの解放は`resource[Symbol.dispose]()`ですから、うっかり下のコードを上のコードにリファクタリングしてしまうと意味が変わります。

```js
try using (const resource = makeResource()) {
  const {read, write} = resource;
  // ...
}
```

ちょっと非直感的な気もしますが、この挙動についてはTC39ミーティングでも議論になっています。経緯が気になる方は議事録を探してみましょう。

### `()`内で複数の変数を宣言する

実は、`try-using`文の中の`const`で複数の変数（変数でなく分割代入でも構いませんが）を宣言することもできます。

```js
try using (const a = makeResource(), b = makeResource()) {
  // ...
}
```

これはおおよそ以下と同じ意味です[^nested_try]。

[^nested_try]: おおよそというのは、スコープの作られ方の違いやエラー処理のされ方（後述）の違いに表れます。

```js
try using (const a = makeResource()) {
  try using (const b = makeResource()) {
    // ...
  }
}
```

注目すべき点は、この場合上の書き方でも下の書き方でも必ず`b`→`a`の順にリソースが解放されるということです。`try-using`で複数のリソースを同時に宣言する場合は宣言と逆順にリソースが解放されるのです。これは次の話とも関わっています。

### リソース初期化中のエラー処理

先ほど、この新しい`try-using`文はエラー処理もできるということを紹介しました。実は、今回エラー処理に指して考えないといけないことが少し増えています。それは、`{ ... }`の中だけでなく`(...)`の中でエラーが発生することがあるということです。

```js
try using (const resource = makeResource()) { // ←ここでエラーが発生したら？
  console.log("1");
} catch (error) {
  console.log("2");
}
```

この例で、`makeResource()`でエラーが発生したらどうなるでしょうか。まず、`console.log("1");`は実行されません。リソース初期化中にエラーが発生した場合は`try-using`のブロックの中身には入らないのです。

そして、ちゃんと`catch`ブロックは実行されます。すなわち、実行結果としては`2`だけ表示されるということになります。

なお、`resource[Symbol.dispose]()`は実行されません。というか、リソースが生成される（`resource`にリソースが代入される）より前にエラーが発生したのでそもそも解放すべきリソースがありませんね。

では、次の例はどうでしょうか。

```js
try using (const resource1 = makeResource(), resouce2 = makeResource()) {
  console.log("1");
} catch (error) {
  console.log("2");
}
```

先ほど紹介した機能を用いて、2つのリソースを一緒に初期化しました。ここで、**2回目の`makeResource`でエラーが発生した場合**を考えましょう。ポイントは、`resource1`は既に初期化が完了している（＝解放する責任がある）ということです。

この場合、まずリソース初期化中にエラーが発生したということで、`try`のブロックの中身は実行されません（`"1"`は表示されません）。

そして、`catch`ブロックに入る**前**に`resource1`が解放される、すなわち`resource1[Symbol.dispose]()`が呼び出されます。あとは先ほどと同じです。

このようなレアケースでもちゃんとリソースが解放できるようになっていてとても偉いですね。

ちなみに、リソース初期化中のエラーにはもうひとつバリエーションがあります。それは、リソースが`[Symbol.dispose]`メソッドを持っていなかった場合です。リソースが`[Symbol.dispose]`メソッドを持っているかどうかはリソースの初期化時にチェックされ、持っていなかった場合は即座にエラーとなります。

### `[Symbol.dispose]`メソッド取得のタイミング

これまでいくつか`try-using`文の注意点を説明しましたが、説明すべきことがもう1つあります（まだ実装もされていないのに時期尚早だとか言わないでくださいね）。

それは、`[Symbol.dispose]`メソッドが取得されるのは**`try`ブロックに入る前**であるということです。すなわち、`try`ブロックの中でリソースの`[Symbol.dispose]`メソッドを書き換えても反映されません。下の例で考えてみましょう。

```js
function makeResource() {
  return {
    [Symbol.dispose]() {
      console.log("disposed!");
    }
  };
}

try using (const resource = makeResource()) {
  // tryブロックの中でリソースの[Symbol.dispose]メソッドを書き換えている
  resource[Symbol.dispose] = () => {
    console.log("Hello, world!");
  };
}
```

この例で`try-using`文から出たときには何が表示されるでしょうか。

答えは`"disposed!"`です。`try-using`にさしかかった瞬間に`resource[Symbol.dispose]`が裏で保存されており、それがリソース解放時に呼び出されます。よって、`[Symbol.dispose]`メソッドをあとで書き換えても反映されないのです。

## リソース解放中のエラー処理と`AggregateError`

実は、この記事で扱っているプロポーザルは、`try-using`文の登場にあわせて新しいエラーオブジェクトをひとつ定義しています。それが`AggregateError`です。これは端的に言えば**複数のエラーをひとつにまとめたもの**で、`try-using`文の処理が始まってから`catch`に到達するまでに複数のエラーが発生するシチュエーションに対応するために導入されたものです。

そのようなシチュエーションが実際に発生するのは**リソースの解放中にエラーが発生した場合**、言い方を変えれば`[Symbol.dispose]`メソッドがエラーを発生させた場合です。

具体例を見てみましょう。

```js
// 開放時にエラーが発生する変なリソースを作る
function makeResource() {
  return {
    [Symbol.dispose]() {
      throw new Error("dispose error");
    }
  };
}

try using (const resource = makeResource()) {
  throw new Error("error in try");
} catch(error) {
  console.log(error); // ←このerrorは何？
}
```

今回`makeResource()`で作られるのは、開放するとエラーが発生するという変なリソースです。上の例を実行するとどうなるか考えてみましょう。

まず、リソースの初期化は問題なく完了して`try`ブロックの中が実行されます。そこには`throw`文が待ち構えており、`error in try`というエラーが投げられます。

エラーが発生したので次は`catch`ブロックに移らないといけませんが、その前に`resource`を解放しなければいけません。そのため`resource[Symbol.dispose]()`が呼び出されます。そうなると、ここで2つ目のエラー`dispose error`が発生することになります。

ということは、`try`ブロック（およびリソース）の処理でエラーが2つ発生してしまいました。このとき`catch`ブロックが受け取るエラーは2つのうちどちらなのでしょうか。

答えは**両方**です。より正確には、両方の情報を持った`AggregateError`オブジェクトが作られそれが`catch`ブロックに渡されます。`AggregateError`オブジェクトは`errors`プロパティを持ち、これがその中に含まれるエラーの配列となっています。

つまり、上の例の`error`はおおよそ次のようなオブジェクトになっています。

```js
AggregateError {
  errors: [Error("error in try"), Error("dispose error")]
}
```

`try`文の場合は配列のエラーは発生した順番に入っています。

いずれにせよ、発生したエラーの情報を漏らすこと無く得られるのはいいデザインですね。時代によっては「最後のエラーオブジェクトのみ`catch`ブロックに渡される」みたいな言語仕様になっていたかもしれません。

余談ですが、複数のエラーをまとめたオブジェクトの需要は[Promise.any](https://github.com/tc39/proposal-promise-any)という別のプロポーザルでも発生しており、`AggregateError`はそちらのプロポーザルにも登場しています。

これからのエラーハンドリングは`AggregateError`の考慮が求められると言えるでしょう。

## `try-using-await`文

実は`try-using`文には`async`関数の中でのみ使える亜種があります。それは`try-using-await`文です。

`try-using-await`文は通常の`try-using`文と同様の挙動をしますが、一つ違いがあります。それは、リソースの解放時に呼ばれるメソッドが`[Symbol.dispose]`ではなく`[Symbol.asyncDispose]`であるということです。

`[Symbol.asyncDispose]`はリソースの解放を非同期的に行うことができます。`[Symbol.asyncDispose]`が返り値として`Promise`を返した場合、`try-using-await`文は自動的にその`Promise`を`await`します。

なお、非同期的な解放のサポートはオプショナルです。つまり、`try-using-await`文の場合でも、リソースが`[Symbol.asyncDispose]`メソッドを持っていなかった場合は通常の`[Symbol.dispose]`が代わりに使用されます。

試しに、解放に1秒かかる変なリソースを作ってみましょう。

```js
function makeResource() {
  return {
    async [Symbol.asyncDispose]() {
      await sleep(1000); // sleep関数は予めいい感じに定義しておく
      console.log("disposed!");
    }
  }
}

async function main() {
  try using await (const resource = makeResource()) {
    console.log("1");
  }
  console.log("2");
}

main();
```

`main`関数の中に注目してください。`try-using-await`でリソースを使っていますが、このプログラムの挙動はどうなるでしょうか。

結果は、まず`1`が表示され、1秒後に`disposed!`と`2`が表示されます。この1秒という待ち時間が`resource[Symbol.asyncDispose]()`の呼び出しに由来するものです。`try-using-await`文により自動的に`resource[Symbol.asyncDispose]()`が呼び出されますが、その返り値は上の定義の通り、1秒後に解決される`Promise`です。`try-using-await`文はこの`Promise`を自動的に`await`するため、`try`ブロックから出るときに1秒待つという挙動になります。

先述のように、リソースが`[Symbol.asyncDispose]`を持たない場合は`[Symbol.dispose]`にフォールバックされます。解放に非同期処理が必要ないリソースはフォールバックさせるとよいでしょう。逆に必ず非同期的に解放しないといけない場合は、`[Symbol.dispose]`をそもそも用意しないか、呼ぶとエラーになるようにするのがよいでしょう。TypeScriptサポートのことまで考えると、そのような場合は上の例のように`[Symbol.dispose]`をそもそも持たないようにするのがベストプラクティスとなりそうです。

それにしても、`try using await`というキーワード3連続は威圧感がありますね。まあ中で暗黙的に何かを`await`する構文は構文中に`await`と明示する方針のようなので仕方ありません。既存の`for-await-of`文も非同期イテレータの`next()`の返り値を暗黙のうちに`await`しています。

## イテレータと`[Symbol.dispose]`

以上で`try-using`及び`try-using-await`文の紹介は終わりです。

このプロポーザルが仕様として導入されれば、既存のオブジェクトやDOM由来のオブジェクトの中にもリソースとして`try-using`文と一緒に利用可能なものが出てくるでしょう。このプロポーザルではその一例として**イテレータ**に対して`[Symbol.dispose]`メソッドが定義されています。

イテレータの`[Symbol.dispose]`メソッドは自身の`return`メソッドを（存在すれば）呼び出すものとして定義されています。ジェネレータ関数により作られたイテレータはちょうど`return`メソッドを持っていますから、ジェネレータ関数との併用が期待されていることが分かります。

ジェネレータ関数によって作られたイテレータの`return`メソッドは、動作中のジェネレータ関数を強制終了させる動作をします[^generator_return]。そうなると何が嬉しいかというと、**ジェネレータ関数が`try-using`文で止まっていた場合そのリソースを解放できます**。

[^generator_return]: 正確には、停止中の`yield`式から`return` completionを発生させます（completionについては筆者の既存記事[JavaScriptの{}を理解する](https://qiita.com/uhyo/items/39750a5359629b3fee05)で詳しく解説しています）。

ちょっと長いですが例を用いて説明します。

```js
function makeResource() {
  return {
    [Symbol.dispose]() {
      console.log(`resource is disposed!`);
    }
  };
}

// リソースからひとつずつデータを読みだす関数（のつもり）
function* readFromResource() {
  try using (const resource = makeResource()) {
    while (true) {
      yield 1;
    }
  }
}

// イテレータをリソースとして使う
try using (const iter = readFromResource()) {
  // 3回データを読んで終わり
  iter.next();
  iter.next();
  iter.next();
}
// ここで "resource is disposed!"と表示される
```

この例では`readFromResource`というジェネレータ関数を定義しました。このジェネレータ関数は内部でリソースを使用しており、`try-using`文の内部に留まって`yield`でデータを発生させ続けます。

例の最後のところで`readFromResource()`を使っています。`readFromResource`はジェネレータ関数なので呼び出すとイテレータが返りますが、このプロポーザルではイテレータが`[Symbol.dispose]`を持っているのでこの例のようにリソースとして`try-using`文で使うことができます。内部では適当にイテレータを使っています。

注目すべきは、イテレータを使い終わった後です。このタイミングで`"resource is disposed!"`と表示されます。

これはどういうことかというと、まず`try-using`文を脱したタイミングで`iter[Symbol.dispose]()`が呼ばれます。それは`iter.return()`を呼び出すため、`readFromResource()`のジェネレータ関数が強制終了します。ということは、`readFromResource`内の`try-using`文から脱出したことになるため`resource[Symbol.dispose]()`の呼び出しが発生します。これが`"resource is disposed!"`というログを表示するのです。

結局この例で解決したかった問題は何かというと、**ジェネレータ関数により作ったイテレータを放置するといつ使い終わったのか分からない**という問題です。

上の例の`readFromResource`ジェネレータ関数は、使い続ける限りはずっと`try-using`文の中にいます。これは、イテレータが使われ続ける限りはずっとリソースを使い続けていることを意味します。イテレータを使い終わったあと（明示的に終了させることなく）この状態で放置されると、このリソースは解放されることがありません。これを防ぐためにイテレータの`return()`メソッドでジェネレータ関数を明示的に強制終了させなければいけないのです。`return()`メソッドが呼び出されるとジェネレータ関数の制御が外に脱出しようとするため、`try-using`文の外に出た扱いとなり無事にリソースが解放されます。

そして、イテレータの`[Symbol.dispose]`メソッドは自動的にこの`return()`メソッドを呼び出すものとなっています。別の言い方をすれば、**イテレータ自体が裏にジェネレータ関数を持っているリソースであり、イテレータを解放することで裏のジェネレータ関数（さらには裏のジェネレータ関数が保持しているリソース）が解放される**ということになります。

今後`try-using`文を使うにあたってはこのような「何がリソースであるか」という考え方が重要になってくるでしょう。イテレータとジェネレータ関数というのは極端な例ですが、何らかのリソースを裏で保持しているオブジェクトは一般に自身もリソースとなり、自分が解放されたら自分が持っているリソースを解放しなければいけません。

そんなに難しくない言語仕様のわりには考えさせられることが多いですね。それだけリソース管理は難しいということでしょう。

## 仕様の変遷

最後に、少しこのプロポーザルの変遷に触れたいと思います。プロポーザルが現在の形になったのはStage 2に上がると同時なので、この先は比較的安定することが予想されます。逆に言えば、Stage 1の間は結構仕様がダイナミックに変化していました。

まず、最初の草案では`try-using`文ではなく単なる`using`文でした。C#と同じ形ですね。

```js:using文時代の例
using (const resource = makeResource()) {
  // ...
}
```

さらに`()`の中は`const`宣言だけでなく式も可能ですから、`using(resource) { ... }`のような形も可能となります。

この案の問題点は、なんと言っても`using`がキーワードではない点でしょう。そのため、`using`という関数を呼び出しているのと紛らわしいという問題が発生します。これは`using(resource)`まで構文解析しても解決できず、そのあとに`{`が来るかどうかまで調べないとどちらなのか分かりません。また、悪名高き自動セミコロン挿入によって、従来のJavaScriptでも次のようなコードが存在し得ます。

```js
using(resource)
{
  // ...
}
```

これは`using(resource)`という関数呼び出しと`{ ... }`というブロックの2つを逐次実行するというプログラムです。機能追加で既存のプログラムの意味が変わるのはまずいですから、必然的に新しい`using`構文では`using(...) { ... }`の`)`と`{`に改行が入ってはいけないというルールが必要になります。これはたいへん分かりにくいし不便ですね。

というような事情があり、`using`のみを用いる案は頓挫しました。

次に出てきたのが`try`だけを用いる案です。この時点で既存の`try`文が持つエラーキャッチ機能が今回のプロポーザルの機能の一部として導入されました。これはJavaの`try`文が持つ機能と同様ですから、一気にC#寄りからJava寄りに変遷したことになります。

```js
try (const resource = makeResource()) {
  // ...
} catch(err) {
  // ...
}
```

`try`は既にキーワードなので既存のプログラムに`try(...)`という関数呼び出しは存在せず、また既存の`try`文は`try { ...`という形で始まるので新構文ともぶつかりません。構文上の曖昧性は回避できています。

ならばこの方向性で進むかと思いきや、今度は既存のエラーをキャッチできる`try`文がある中で`try`とだけ書いてリソース管理ができるのはさすがに分かりにくいという問題が挙がりました。

ということで、分かりやすさと構文上の問題点を考慮した結果、最終的にJavaとC#の折衷とも言える`try using`文が誕生したのです。めでたしめでたし。

ちなみに、ここでは構文の変化を取り上げましたが他にも細かい仕様があっちに行ったりこっちに行ったりしていす。具体的には`try using`文の`()`の中で分割代入したとき何がリソースとして扱われるのか（現在の仕様では分割代入で作られた変数ひとつひとつがリソースですが、当初の案では分解される前のオブジェクトがリソースとして扱われていました）、またリソースに`[Symbol.dispose]`メソッドが無かった場合エラーはいつ発生するのか（現在の仕様では`try`ブロック開始時ですが、当初の案ではリソース解放時でした）といった違いがあります。細かい話なので興味がある方は調べてみましょう。

## まとめ

この記事では、JavaScriptにリソースという概念を持ち込む[Explicit Resource Management](https://github.com/tc39/proposal-explicit-resource-management)というプロポーザルを紹介しました。筆者はC#はやらないのですがC#の`using`文は結構いいという話を聞きますから、きっとJavaScriptの`try-using`文もいい感じなのだろうと信じています。

このプロポーザルの登場によって、将来的にJavaScriptプログラムにおけるリソース管理というのがどのような形になるのかが見えてきました。いくつかの例を通してご紹介した通り、`try-using`文という便利な構文があってもなおリソース管理には考えることが結構あります。ぜひこの記事を通して今のうちにリソースの概念を理解しておきましょう。このプロポーザルがStage3くらいになれば、利用例に`try using (const thing = new Something())`なんて書いてあるライブラリが出てきてもおかしくありません。いざその時になっても怯まないように備えておきたいものですね。

### 関連記事

ある意味この記事と関連している筆者の既存記事をひとつご紹介します。

- [WeakRef: JavaScriptに弱参照がやってくる（ついでにFinalizationも）](https://qiita.com/uhyo/items/5dc97667ba90ce3941cd)

この記事では`WeakRef`という別のプロポーザルを紹介しています。こちらが構文上のexplicit（明示的）なリソース管理ならあちらはGCによるimplicit（暗黙的）なリソース管理と言えるかもしれません。