Disposable
- Disposable 是一个非常重要和基础的概念的,它贯穿了整个 vscode 项目中,90% 的对象都是继承 Disposable,还有大量的实现 IDisposable 接口的对象。 Disposable 本身并没有做太多事情: 它是一个抽象类,提供了两个方法 _register (protected) 和 dispose (public), 可以通过 dispose 方法把 _register 注册的 listener (IDispsable 对象) 给全部销毁。其核心的工作就是将继承Disposable的对象管理起来,再其销毁的时候销毁掉这个对象以及其依赖的对象,提高内存的利用率。
- 为了保证插件的高效运行,VS Code使用了Dispose模式,大部分插件API都实现了IDisposable接口,生成的对象则会拥有一个dispose函数属性。
typescript
interface IDisposable {
dispose(): void;
}
- Dispose模式主要用来资源管理,资源比如内存被对象占用,则会通过调用方法来释放,这些方法通常被命名为'close','dispose','free','release'。一个著名的例子便是C#,C#通过Dipose Pattern来释放不受CLR(Common Language Runtime)管理的非托管资源。
- Javascript的内存分配是通过GC(garbage collector)进行管理,大部分情况下它都是自动执行且对用户不可见的。然而这种自动化的管理方式却存在一个潜在的问题,就是Javascript开发者会错误的认为他们不需要再关心内存管理了,从而再无意间书写一些不利于内存回收的代码。
- 所以,最清楚被分配的内存在未来是否需要使用的还是开发者,但是每次使用完一个对象后就手动的将其销毁,这样的做法即不高效,也不可靠。正因为此,VS Code使用了Dispose Pattern来管理对象销毁。当扩展功能执行时,Extension Host会在正确的时机调用dispose方法,销毁Code生成的对象,减少内存使用。比如说,方法'setStatusBarMessage(value: string)'返回一个'Disposable'对象,当调用dispose方法的时候会移除掉信息对象。
typescript
// 第一个重载参数是单个disposable类型
function dispose<T extends IDisposable>(disposable: T): T;
// 第二个重载参数是多个disposable类型传参数,参数可能为undefined。
function dispose<T extends IDisposable>(...disposables: Array<T | undefined>): T[];
// 第三个重载参数是一个disposable类型的数组。
function dispose<T extends IDisposable>(disposables: T[]): T[];
// 第三个重载参数为两种,第一个是disposable类型或disposable数组类型,剩余的为disposable类型。
function dispose<T extends IDisposable>(first: T | T[], ...rest: T[]): T | T[] | undefined {
// 如果第一个参数是数组,则依次调用传参数的dispose方法
if (Array.isArray(first)) {
first.forEach(d => d && d.dispose());
// 返回空的数组
return [];
} else if (rest.length === 0) {
// 如果没有没有剩余参数
if (first) {
// 如果存在first
// 调用第一个dispose
first.dispose();
// 返回first
return first;
}
return undefined;
} else {
// first不是数组,且rest长度不为0
dispose(first);
dispose(rest);
// 返回空数组
return [];
}
}
// implement IDisposable 的Disposable 抽象类
abstract class Disposable implements IDisposable {
// Disposable类的静态对象,用于返回一个包含空的dispose方法的IDisposable对象。dispose被执行了,则表示该对象不再需要了。
// 部分基础API使用了该对象,用于标志资源释放。
static None = Object.freeze<IDisposable>({ dispose() { } });
// protected属性toDispose返回protected对象_toDispose, 该对象初始值是一个空的数组。
protected _toDispose: IDisposable[] = [];
// 返回IDisposable数组。
protected get toDispose(): IDisposable[] { return this._toDispose; }
// 设置状态标志,表示该对象是否有被销毁。
private _lifecycle_disposable_isDisposed = false;
// 暴露公共方法dispose,执行完后将_lifecycle_disposable_isDisposed状态标志设为true,同时调用lifecycle内的dispose方法处理_toDispose数组,并重新赋值空数组。
public dispose(): void {
this._lifecycle_disposable_isDisposed = true;
this._toDispose = dispose(this._toDispose);
}
// 内部方法注册实例,若_lifecycle_disposable_isDisposed为true,则表明该方法已经被dispose过,则不能再使用,需dispose掉,否则,推入_toDispose数组。
protected _register<T extends IDisposable>(t: T): T {
// 判断这个对象有没有被dispose过
if (this._lifecycle_disposable_isDisposed) {
console.warn('Registering disposable on object that has already been disposed.');
t.dispose();
} else {
this._toDispose.push(t);
}
return t;
}
}
Event和Emitter
- 在vscode中事件模块是一个比较基础,而且比较核心的一块内容,可以说是vscode应用程序的一块基石。
Event事件
- Event 接口规定了一个函数,当调用了这个函数,就表示监听了这个函数所对应的事件流。
-
- listener 参数是事件派发时将会被调用的回调函数,参数 e 为单个事件,换句话说, listener 就是事件的消费者
- thisArgs 参数是回调函数中 this 所指向的对象
- disposables
typescript
export interface Event<T> {
(listener: (e: T) => any, thisArgs?: any, disposables?: IDisposable[] | DisposableStore): IDisposable;
}
- 返回的 IDisposable 对象用于解除这个监听的(通过调用它的 dispose 方法)。
- 另外一种解除监听的方式就是 disposable 了,Event 函数在执行的过程中会将 IDisposable 插入 disposables,方便调用方决定在什么时候解除监听。
- 在VSCode源码的实现中,实现了一个Event的库。里面包含了很多其他的有关Event事件的方法。
Emitter事件发射器
- Emitter 类型暴露了两个重要方法:
-
- fire,从这个方法的函数签名就能看出它就是用来派发一个事件的,该方法的主要逻辑就是将 this._listeners 当中的保存的 listener 全部调用一遍(省略了部分分支逻辑和性能监控相关代码)
csharp
fire(event: T): void {
if (this._listeners) {
for (let listener of this._listeners) {
this._deliveryQueue.push([listener, event]);
}
while (this._deliveryQueue.size > 0) {
const [listener, event] = this._deliveryQueue.shift()!;
try {
if (typeof listener === 'function') {
listener.call(undefined, event);
} else {
listener[0].call(listener[1], event);
}
} catch (e) {
onUnexpectedError(e);
}
}
}
}
-
- get event(),这个方法会在 Emitter 中创建一个 Event,其主要逻辑就是将 listener 添加到 this._listeners 当中
arduino
const remove = this._listeners.push(!thisArgs ? listener : [listener, thisArgs]);
- Emitter 类型还提供了一些特殊的回调接口:
typescript
export interface EmitterOptions {
onFirstListenerAdd?: Function;
onFirstListenerDidAdd?: Function;
onListenerDidAdd?: Function;
onLastListenerRemove?: Function;
}
-
这使得 Emitter 在注册消费者的时候执行一些额外的逻辑。
-
事件的使用方式主要包括:
- 注册事件发射器
- 对外提供定义的事件
- 在特定时机向订阅者触发事件
-
在Emitter的实现过程中,还实现了其他的一些场景,比如防抖、once等等。具体的解析可以参考:github.com/wzhudev/blo...
简单的Event-Emitter使用
- 事件的定义:(生产一个事件)
csharp
export class EditorService extends Disposable implements EditorServiceImpl {
declare readonly _serviceBrand: undefined;
//#region events
private readonly _onDidActiveEditorChange = this._register(new Emitter<void>());
readonly onDidActiveEditorChange = this._onDidActiveEditorChange.event;
private readonly _onDidVisibleEditorsChange = this._register(new Emitter<void>());
readonly onDidVisibleEditorsChange = this._onDidVisibleEditorsChange.event;
private readonly _onDidEditorsChange = this._register(new Emitter<IEditorsChangeEvent>());
readonly onDidEditorsChange = this._onDidEditorsChange.event;
private readonly _onDidCloseEditor = this._register(new Emitter<IEditorCloseEvent>());
readonly onDidCloseEditor = this._onDidCloseEditor.event;
//#endregion
}
- 事件的消费:(注册监听函数)
kotlin
import { Event } from 'vs/base/common/event';
class MainThreadDocumentAndEditorStateComputer {
constructor(
@IEditorService private readonly _editorService: IEditorService,
) {
this._editorService.onDidActiveEditorChange(_ => this._updateState(), this, this._toDispose);
Event.filter(this._paneCompositeService.onDidPaneCompositeOpen, event => event.viewContainerLocation === ViewContainerLocation.Panel)(_ => this._activeEditorOrder = ActiveEditorOrder.Panel, undefined, this._toDispose);
Event.filter(this._paneCompositeService.onDidPaneCompositeClose, event => event.viewContainerLocation === ViewContainerLocation.Panel)(_ => this._activeEditorOrder = ActiveEditorOrder.Editor, undefined, this._toDispose);
this._editorService.onDidVisibleEditorsChange(_ => this._activeEditorOrder = ActiveEditorOrder.Editor, undefined, this._toDispose);
}
}