Jelajahi Sumber

feat(utils): DisposableCollection auto remove (#330)

xiamidaxia 7 bulan lalu
induk
melakukan
bb623e9fe6

+ 43 - 0
packages/common/utils/src/disposable.spec.ts

@@ -104,4 +104,47 @@ describe('disposable', () => {
     expect(disposedRet).toEqual([1]);
     expect(isDisposed).toBeTruthy();
   });
+  test('DisposableCollection auto remove', () => {
+    const dc = new DisposableCollection();
+    const dc2 = new DisposableCollection();
+    const disposable1: Disposable = {
+      dispose() {},
+    };
+    const originalDispose = disposable1.dispose;
+    dc.push(disposable1);
+    dc2.push(disposable1);
+    expect(originalDispose === disposable1.dispose).toBeFalsy();
+    disposable1.dispose();
+    expect(dc.length).toEqual(0);
+    expect(dc2.length).toEqual(0);
+    expect(originalDispose === disposable1.dispose).toBeTruthy();
+  });
+  test('DisposableCollection push cancel', () => {
+    const dc = new DisposableCollection();
+    const disposable1: Disposable = {
+      dispose() {},
+    };
+    const originalDispose = disposable1.dispose;
+    const cancel = dc.push(disposable1);
+    expect(originalDispose === disposable1.dispose).toBeFalsy();
+    cancel.dispose();
+    expect(originalDispose === disposable1.dispose).toBeTruthy();
+  });
+  test('DisposableCollection auto remove nested', () => {
+    const dc1 = new DisposableCollection();
+    const dc2 = new DisposableCollection();
+    const disposable1: Disposable = {
+      dispose() {},
+    };
+    dc1.push(disposable1);
+    dc2.push(dc1);
+    dc2.push(disposable1);
+    dc1.dispose();
+    expect(dc2.length).toEqual(0);
+  });
+  test('DisposableCollection push Disposbale.NULL', () => {
+    const dc = new DisposableCollection();
+    dc.push(Disposable.NULL);
+    expect(dc.length).toEqual(0);
+  });
 });

+ 16 - 11
packages/common/utils/src/disposable.ts

@@ -47,7 +47,7 @@ export class DisposableImpl implements Disposable {
 }
 
 export class DisposableCollection implements Disposable {
-  protected readonly disposables: (Disposable & { _origin: Disposable })[] = [];
+  protected readonly disposables: Disposable[] = [];
 
   protected readonly onDisposeEmitter = new Emitter<void>();
 
@@ -57,6 +57,10 @@ export class DisposableCollection implements Disposable {
     toDispose.forEach((d) => this.push(d));
   }
 
+  get length() {
+    return this.disposables.length;
+  }
+
   get onDispose(): Event<void> {
     return this.onDisposeEmitter.event;
   }
@@ -86,25 +90,26 @@ export class DisposableCollection implements Disposable {
 
   push(disposable: Disposable): Disposable {
     if (this.disposed) return Disposable.NULL;
+    if (disposable === Disposable.NULL) {
+      return Disposable.NULL;
+    }
     const { disposables } = this;
-    if (disposables.find((d) => d._origin === disposable)) {
+    if (disposables.find((d) => d === disposable)) {
       return Disposable.NULL;
     }
-    let disposableWrap: Disposable & { _origin: Disposable };
+    const originalDispose = disposable.dispose;
     const toRemove = Disposable.create(() => {
-      const index = disposables.indexOf(disposableWrap);
+      const index = disposables.indexOf(disposable);
       if (index !== -1) {
         disposables.splice(index, 1);
       }
+      disposable.dispose = originalDispose;
     });
-    disposableWrap = {
-      dispose: () => {
-        toRemove.dispose();
-        disposable.dispose();
-      },
-      _origin: disposable,
+    disposable.dispose = () => {
+      toRemove.dispose();
+      disposable.dispose();
     };
-    disposables.push(disposableWrap);
+    disposables.push(disposable);
     return toRemove;
   }