disposable-collection.ts 2.2 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495
  1. /**
  2. * Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
  3. * SPDX-License-Identifier: MIT
  4. */
  5. import { Emitter, Event } from './event';
  6. import { Disposable } from './disposable';
  7. export class DisposableImpl implements Disposable {
  8. readonly toDispose = new DisposableCollection();
  9. dispose(): void {
  10. this.toDispose.dispose();
  11. }
  12. get disposed(): boolean {
  13. return this.toDispose.disposed;
  14. }
  15. get onDispose(): Event<void> {
  16. return this.toDispose.onDispose;
  17. }
  18. }
  19. export class DisposableCollection implements Disposable {
  20. protected readonly disposables: Disposable[] = [];
  21. protected readonly onDisposeEmitter = new Emitter<void>();
  22. private _disposed = false;
  23. constructor(...toDispose: Disposable[]) {
  24. toDispose.forEach((d) => this.push(d));
  25. }
  26. get length() {
  27. return this.disposables.length;
  28. }
  29. get onDispose(): Event<void> {
  30. return this.onDisposeEmitter.event;
  31. }
  32. get disposed(): boolean {
  33. return this._disposed;
  34. }
  35. dispose(): void {
  36. if (this.disposed) {
  37. return;
  38. }
  39. this._disposed = true;
  40. this.disposables
  41. .slice()
  42. .reverse()
  43. .forEach((disposable) => {
  44. try {
  45. disposable.dispose();
  46. } catch (e) {
  47. console.error(e);
  48. }
  49. });
  50. this.onDisposeEmitter.fire(undefined);
  51. this.onDisposeEmitter.dispose();
  52. }
  53. push(disposable: Disposable): Disposable {
  54. if (this.disposed) return Disposable.NULL;
  55. if (disposable === Disposable.NULL) {
  56. return Disposable.NULL;
  57. }
  58. const { disposables } = this;
  59. if (disposables.find((d) => d === disposable)) {
  60. return Disposable.NULL;
  61. }
  62. const originalDispose = disposable.dispose;
  63. const toRemove = Disposable.create(() => {
  64. const index = disposables.indexOf(disposable);
  65. if (index !== -1) {
  66. disposables.splice(index, 1);
  67. }
  68. disposable.dispose = originalDispose;
  69. });
  70. disposable.dispose = () => {
  71. toRemove.dispose();
  72. disposable.dispose();
  73. };
  74. disposables.push(disposable);
  75. return toRemove;
  76. }
  77. pushAll(disposables: Disposable[]): Disposable[] {
  78. return disposables.map((disposable) => this.push(disposable));
  79. }
  80. }