第一篇: vscode源码分析【一】从源码运行vscode
第二篇:vscode源码分析【二】程序的启动逻辑,第一个窗口是如何创建的
第三篇:vscode源码分析【三】程序的启动逻辑,性能问题的追踪
第四篇:vscode源码分析【四】程序启动的逻辑,最初创建的服务

在上一篇中,我们看到lifecycleService监听了很多electron原生的事件,
监听了之后,一旦事件被触发,vscode是怎么派发这些事件的呢?

在入口程序的startup方法中(src\vs\code\electron-main\main.ts),有这么一句:

once(lifecycleService.onWillShutdown)(() => (configurationService as ConfigurationService).dispose());

上面这句话语义好直白呀!一旦lifecycle里发生了willShutdown的事件,就执行后面的回调函数!
那我们看看lifecycle里的这个onWillShutdown(src\vs\platform\lifecycle\electron-main\lifecycleMain.ts)

private readonly _onWillShutdown = this._register(new Emitter<ShutdownEvent>());
readonly onWillShutdown: Event<ShutdownEvent> = this._onWillShutdown.event;

发现它是被_register注册的,这个文件里并没有_register函数,函数在它的父类Disposable里(src\vs\base\common\lifecycle.ts)
我一直以为这是资源释放的类,没想到还有事件相关的内容,哈!

private readonly _store = new DisposableStore();
protected _register<T extends IDisposable>(t: T): T {
if ((t as any as Disposable) === this) {
throw new Error('Cannot register a disposable on itself!');
}
return this._store.add(t);
}

看来,还得看DisposableStore的add方法:

	public add<T extends IDisposable>(t: T): T {
if (!t) {
return t;
}
if ((t as any as DisposableStore) === this) {
throw new Error('Cannot register a disposable on itself!');
} markTracked(t);
if (this._isDisposed) {
console.warn(new Error('Registering disposable on object that has already been disposed of').stack);
t.dispose();
} else {
this._toDispose.add(t);
} return t;
}

markTracked这个方法不用管,里面什么也没干!
_toDispose就是个set,用来存你传入的事件的;
另外,这个函数有个特别之处,就是你喂了它什么它就拉了什么出来!
因为我们喂了它一个Emitter的实例,那我们就去看看Emitter(src\vs\base\common\event.ts)
这是个泛型类型
有个get属性:

get event(): Event<T> { //......

上面说的:

this._onWillShutdown.event;

取.event的时候,执行的就是这里,它其实返回了一个方法:

this._event = (listener: (e: T) => any, thisArgs?: any, disposables?: IDisposable[] | DisposableStore) => { //......

好!打住!看到这里我们先不去看这个方法的具体逻辑,
先返回头来看最开始时main.ts里的那个once方法:(src\vs\base\common\functional.ts)

export function once<T extends Function>(this: any, fn: T): T {
const _this = this;
let didCall = false;
let result: any; return function () {
if (didCall) {
return result;
} didCall = true;
result = fn.apply(_this, arguments); return result;
} as any as T;
}

很好理解,传入一个方法,返回一个方法,
我们知道,我们传入的是:

lifecycleService.onWillShutdown

前面我们说了,它确实是一个方法;
这个once还返回了一个匿名函数;
我们通过这个匿名函数,把我们的事件处理逻辑,绑定给了:lifecycleService.onWillShutdown
这是绑定的关键代码:

result = fn.apply(_this, arguments);

OK!我们再去看那个this._event返回的方法具体干了啥?!
传入的参数,listener是我们的匿名回调函数

() => (configurationService as ConfigurationService).dispose()

Emitter实例的_listeners属性已经在别处初始化成了LinkedList的实例;

const remove = this._listeners.push(!thisArgs ? listener : [listener, thisArgs]);

这句话把我们的匿名回调函数加到这个LinkedList中去了
好,以上是绑定事件,
我们再来看看这个事件被触发的时候是怎样的

this._onWillShutdown.fire({
join(promise) {
if (promise) {
joiners.push(promise);
}
}
});

在这个fire方法中:

			for (let iter = this._listeners.iterator(), e = iter.next(); !e.done; e = iter.next()) {
this._deliveryQueue.push([e.value, 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);
}
}

循环派发了所有注册的事件

vscode源码分析【五】事件分发机制的更多相关文章

  1. vscode源码分析【九】窗口里的主要元素

    第一篇: vscode源码分析[一]从源码运行vscode 第二篇:vscode源码分析[二]程序的启动逻辑,第一个窗口是如何创建的 第三篇:vscode源码分析[三]程序的启动逻辑,性能问题的追踪 ...

  2. vscode源码分析【八】加载第一个画面

    第一篇: vscode源码分析[一]从源码运行vscode 第二篇:vscode源码分析[二]程序的启动逻辑,第一个窗口是如何创建的 第三篇:vscode源码分析[三]程序的启动逻辑,性能问题的追踪 ...

  3. vscode源码分析【七】主进程启动消息通信服务

    第一篇: vscode源码分析[一]从源码运行vscode 第二篇:vscode源码分析[二]程序的启动逻辑,第一个窗口是如何创建的 第三篇:vscode源码分析[三]程序的启动逻辑,性能问题的追踪 ...

  4. vscode源码分析【六】服务实例化和单例的实现

    第一篇: vscode源码分析[一]从源码运行vscode 第二篇:vscode源码分析[二]程序的启动逻辑,第一个窗口是如何创建的 第三篇:vscode源码分析[三]程序的启动逻辑,性能问题的追踪 ...

  5. vscode源码分析【四】程序启动的逻辑,最初创建的服务

    第一篇: vscode源码分析[一]从源码运行vscode 第二篇:vscode源码分析[二]程序的启动逻辑,第一个窗口是如何创建的 第三篇:vscode源码分析[三]程序的启动逻辑,性能问题的追踪 ...

  6. vscode源码分析【三】程序的启动逻辑,性能问题的追踪

    第一篇: vscode源码分析[一]从源码运行vscode 第二篇:vscode源码分析[二]程序的启动逻辑,第一个窗口是如何创建的 启动追踪 代码文件:src\main.js 如果指定了特定的启动参 ...

  7. Solr4.8.0源码分析(19)之缓存机制(二)

    Solr4.8.0源码分析(19)之缓存机制(二) 前文<Solr4.8.0源码分析(18)之缓存机制(一)>介绍了Solr缓存的生命周期,重点介绍了Solr缓存的warn过程.本节将更深 ...

  8. Solr4.8.0源码分析(18)之缓存机制(一)

    Solr4.8.0源码分析(18)之缓存机制(一) 前文在介绍commit的时候具体介绍了getSearcher()的实现,并提到了Solr的预热warn.那么本文开始将详细来学习下Solr的缓存机制 ...

  9. monkey源码分析之事件注入方法变化

    在上一篇文章<Monkey源码分析之事件注入>中,我们看到了monkey在注入事件的时候用到了<Monkey源码分析番外篇之Android注入事件的三种方法比较>中的第一种方法 ...

随机推荐

  1. Cesium专栏-气象要素(温度、降水)色斑图制作

    Cesium Cesium 是一款面向三维地球和地图的,世界级的JavaScript开源产品.它提供了基于JavaScript语言的开发包,方便用户快速搭建一款零插件的虚拟地球Web应用,并在性能,精 ...

  2. iOS中去除重复的数据

    本人只用了其中一个功能: 需求:一个已知数组arr,判断一个新字符str是否已经存在于arr中,如果不存在,则存入数组arr中 //去重 if (![arr containsObject:str]) ...

  3. js中关于带数字类型参数传参丢失首位数字0问题

    最近在项目中遇到一个问题,js中传带有数字的参数时,如果参数开头有数字0,会把0给去掉. 例如: 方法abc(0123456,789); 方法abc中获取的参数0123456就会变为123456. 原 ...

  4. 码农-->工程师

    微信公众号推送文章记录,侵删 一个猎人的比喻: 当土著拿到猎枪之后,他们射箭的技能退化严重,但因为食物更多了,厨艺有了长足的进展. 当你不再为一些问题担心之后,你就可以把注意力集中在另外一些问题上了. ...

  5. VS2017无法打开Razor视图文件提示:引发类型"System.Exception"异常

    背景介绍 由于电脑装了R#(吃内存大户),导致VS2017打开项目慢以及卡顿,因此在扩展和更新这个功能里面将没用的插件关闭了,导致.NET CORE的Razor视图文件打不开(真心是一脸懵逼,关个插件 ...

  6. Python通过pymysql连接数据库并进行查询和更新SQL方法封装

    1.通过pymysql连接数据库并进行数据库操作2.查询数据3.更新修改SQL方法封装 import pymysql.cursors import json class OperationMysql: ...

  7. 10-Node.js学习笔记-异步函数

    异步函数 异步函数是异步编程语法的终极解决方案,它可以让我们将异步代码写成同步的形式,让代码不再有回调函数嵌套,是代码变得清晰明了 const fn = async()=>{} async fu ...

  8. Office批量打印助手(Excel 批量打印、Word 批量打印)

    最新版本:1.0.6664.34636(更新日期:2018年3月31日) 下载地址:点击下载  程序简介: 本程序能批量打印 Word 文件.Excel 工作簿. 使用程序前请先安装 .NET Fra ...

  9. 品Spring:注解终于“成功上位”

    历史还是抛弃了XML,当它逐渐尝到注解的甜头之后. 尤其是在Spring帝国,到处充满着注解的气息. 注解从一个提供附属信息的“门客”,蜕变为颇具中流砥柱的“君侯”. 注解成功登上了帝国的舞台,定会像 ...

  10. TypeScript 装饰器的执行原理

    装饰器本质上提供了对被装饰对象 Property​ Descriptor 的操作,在运行时被调用. 因为对于同一对象来说,可同时运用多个装饰器,然后装饰器中又可对被装饰对象进行任意的修改甚至是替换掉实 ...