背景:
假设当前有一个主进程A
,一个子进程B
和一个DemoTask
。
我们期望的是,在主进程A
中通过IPC向子进程B
发送一条指令,然后在子进程B
中实例化DemoTask
,并执行DemoTask
中的的逻辑。
在TS中,并没有类似Java反射风格的调用机制,因此需要寻找一种适合TS的动态实例化的方法。
本文主要介绍在子进程B中如何动态实例化DemoTask,不涉及IPC通信的相关内容。
要求:
主进程A
中不能import DemoTask
,即不能在主进程中加载任何DemoTask
的资源。
应该在子进程B
中加载DemoTask
的相关资源,并执行逻辑。
实现
思想 :
在子进程中做一个<类名 - 类>
的映射,然后根据"名称 ",找到具体的"类 ",最后直接"new"即可
举例1:极简实现
ts
// DemoTask.ts
// 定义Task
export class DemoTask {
// 略
}
//////////////////////////////////////////////////////////////////////////////
// TaskRepo.ts
import { DemoTask } from 'DemoTask'
// 关键:注册具体的Task实现类,用于在子进程中实例化具体的Task
// 在子进程中调用
export const TASK_REPO = {}
export function registerTask() {
TASK_REPO["DemoTask"] = DemoTask;
}
//////////////////////////////////////////////////////////////////////////////
// Demo.ts
import { TASK_REPO } from 'TaskRepo'
// 关键:实例化具体的Task
// 在子进程中调用
export function newTask() {
let taskInstance = (new TASK_REPO["DemoTask"])();
// 可以调用Task中的具体方法,略...
return taskInstance
}
关键点:
- 在子进程B起来的时候,需要先执行
registerTask()
,然后才能执行newTask()
进行实例化 - 需要手动维护
TASK_REPO
这个对象:当需要定义新的Task时,需要手动在此处补充Task属性
举例2:结合注解装饰器,实现自动注册
TS
// DemoDecorator.ts
/**
* 关键:注册具体的Task实现类,用于在子进程中实例化具体的Task
* 在子进程中调用
*/
export const TASK_REPO = {}
/**
* 关键:定义注解
* 该 注解(装饰器)可以向 TASK_REPO 中自动注册Task实现类
* 在子进程中调用
*/
export function ProcessTask() {
return (target): void => {
TASK_REPO[target.name] = target;
};
}
/////////////////////////////////////////////////////////////////////////////////////
// DemoTask.ts
// 定义Task:使用装饰器,实现自动注册Task
@ProcessTask()
export class DemoTask {
// 略
}
/////////////////////////////////////////////////////////////////////////////////////
// Demo.ts
// 关键:实例化具体的Task
// 在子进程中调用
export function newTask() {
let taskInstance = (new TASK_REPO["DemoTask"])();
// 可以调用Task中的具体方法,略...
return taskInstance
}
关于这个例子需要进一步讨论:
-
Q:在子进程中,类装饰器为什么没有触发执行?
A:当加载DemoTask
类时,会自动触发类装饰器;若没有触发类装饰器,说明DemoTask
没有被加载到该进程。 -
Q:那该怎么办?
A:在进程起来的时候执行一段"携带该Task的import代码 ",如,打印该Task的类名TSimport {DemoTask} from DemoTask.ts export const TASK_LOADER = [ DemoTask ] // 在 子进程B 起来时,先调用执行这一段代码 export function printDetail() { let array = PAF_TASK_LOADER let taskNames = "" for (let task of array) { taskNames += (task.name) + " ## " } Logger.info(TAG, "succeed loading TASK. detail: length=" + array.length + " taskNames=" + taskNames) }
-
Q:"携带该Task的import代码"需要手写?
A:可以开发"编译插件"
,在编译阶段自动生成上述代码 -
Q:那在"举例1"的基础上直接开发
"编译插件"
,实现自动注册,岂不是更简单
A:是的。但是引入装饰器,既可以在未来扩展DemoTask的功能,同时也不会使"编译插件"开发得过于复杂