vscode中的基本概念(一)

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);
 }
}
相关推荐
代码小学僧2 天前
🌟好看又好用的画图工具分享
前端·开源·设计
小兵张健2 天前
Cursor Figma MCP 互联网最全安装指南
设计·cursor·mcp
用户231434978143 天前
动手制作一个MCP:从零构建简易MCP系统,理解核心组件设计
人工智能·设计·mcp
XH2764 天前
Android 使用 Vector Asset 用法详解
前端·设计
XH2764 天前
Android TintList用法详解
前端·设计
XH2764 天前
ColorStateList 用法详解
前端·设计
XH2764 天前
Android 资源管理全解析:Color、String、Style、Dimen、Array
前端·设计
gyratesky4 天前
使用SD+LoRA训练IP角色出图
aigc·设计
菜菜的后端私房菜4 天前
反射太慢了?那是你不会用LambdaMetafactory!
java·后端·设计