• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// Copyright (C) 2023 The Android Open Source Project
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//      http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15// An (almost) ESnext spec-compliant shim/polyfill/replacement for:
16// - Disposable
17// - AsyncDisposable
18// - DisposableStack
19// - AsyncDisposableStack
20//
21// Typescript 5.2 introduces support for ESnext's explicit resource management,
22// but upgrading to it is not straightforward. However, because we still want to
23// take advantage of disposables, this file acts as a shim, implementing the
24// basics of disposables, which can be used in the meantime.
25//
26// See:
27// - https://github.com/tc39/proposal-explicit-resource-management
28// - https://www.typescriptlang.org/docs/handbook/release-notes/typescript-5-2.html
29
30// Represents an object that can/should be disposed of to release resources or
31// perform cleanup operations.
32export interface Disposable {
33  dispose(): void;
34}
35
36export interface AsyncDisposable {
37  disposeAsync(): Promise<void>;
38}
39
40// A collection of Disposables.
41// Disposables can be added one by one, (e.g. during the lifecycle of a
42// component) then can all be disposed at once (e.g. when the component
43// is destroyed). Resources are disposed LIFO.
44export class DisposableStack implements Disposable {
45  private resources: Disposable[];
46
47  constructor() {
48    this.resources = [];
49  }
50
51  use(d: Disposable) {
52    this.resources.push(d);
53  }
54
55  defer(onDispose: () => void) {
56    this.use({
57      dispose: onDispose,
58    });
59  }
60
61  dispose() {
62    while (true) {
63      const d = this.resources.pop();
64      if (d === undefined) {
65        break;
66      }
67      d.dispose();
68    }
69  }
70}
71
72export class AsyncDisposableStack implements AsyncDisposable {
73  private resources: AsyncDisposable[] = [];
74
75  use(d: AsyncDisposable) {
76    this.resources.push(d);
77  }
78
79  defer(onDispose: () => Promise<void>) {
80    this.use({
81      disposeAsync: onDispose,
82    });
83  }
84
85  async disposeAsync(): Promise<void> {
86    while (true) {
87      const d = this.resources.pop();
88      if (d === undefined) {
89        break;
90      }
91      await d.disposeAsync();
92    }
93  }
94}
95