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