JavaScriptのExplicit Resource Managementという提案では using
という構文が注目されがちな気がする。しかし実は構文以外にもいくつかのビルトインが追加される。
こないだExplicit Resource Managementで追加されたビルトインをSafariに実装したときに「へ〜」と思ったのでよくありそうな使い方を紹介する。
Go の defer
Go の defer
をご存知だろうか。たとえばこういうコードがあるとする。
func main() {
f := createFile("/tmp/defer.txt")
defer closeFile(f)
writeFile(f)
}
main
関数内の2行目では defer closeFile(f)
を実行している。ここで、この closeFile(f)
は createFile
の直後には呼び出されない。代わりにこのmain
関数のスコープを抜ける時、つまりここでは writeFile(f)
の直後に呼び出される。
defer
を使うことでリソースの確保と解放をコード上の近い場所に書くことができる。
JavaScript の using
と DisposableStack#defer
JavaScriptでもExplicit Resouce Managementによって追加される using
という構文と DisposableStack
というビルトインを使うことによって、Goのdefer
のようにリソースの確保と解放の処理を記述できる。
たとえば、こういうふうに書くことができる。
function main() {
using stack = new DisposableStack();
const f = createFile("/tmp/defer.txt");
stack.defer(() => { closeFile(f); });
writeFile(f);
}
このコードは前述したGoのdefer
の例をそのままJavaScriptに移植したものだ。
まず main
関数の先頭で using
構文を使って DisposableStack
のインスタンス stack
を作る。こうすると main
関数のスコープを抜ける時に stack[Symbol.dispose]
関数が呼び出されるようになる。これは using
構文の機能である。
DisposableStack
は内部にスタックを持つデータ構造だ。スタックのエントリはそれぞれ [Symbol.dispose]
関数を持つオブジェクトである。そして DisposableStack
自体の [Symbol.dispose]
関数はスタック内の全てのエントリの [Symbol.dispose]
関数を呼び出す。
stack.defer(fn)
は、 [Symbol.dispose]
が呼び出された時に fn
を呼び出すだけの disposable stack を新たに作成して stack
の内部スタックに積む。
つまり、main
関数のスコープを抜ける時にはまず stack[Symbol.dispose]
が呼び出される。その呼び出しによって stack
の内部スタック内の全てのオブジェクトの [Symbol.dispose]
が呼び出される。この時スタック内には () => { closeFile(f); }
を呼び出すだけのオブジェクトが存在しているため、その関数が呼び出される。
このように using
と DisposableStack
を使うことでJavaScript においても Go の defer
のようにリソースの確保と解放の処理を記述できる。
まとめ
- Go の
defer
を使うことでリソースの確保と解放をコード上の近い場所に置ける - JavaScript でも Explicit Resource Management 提案の
using
構文とDisposableStack#defer
を使うと、似たように書くことができる
参考
Views: 0