在 Cordis 中,管理插件与上下文生命周期的核心引擎并不是传统应用中的全局注册表,而是一个名为 Fiber 的状态机与执行单元。作为 Context 和 Plugin 生命周期的真正管理者。每一个被加载的插件实例,在 Cordis 运行时中都会被包裹在一个独立的 Fiber 作用域内。Fiber 充当了状态机的角色,负责监控插件的依赖是否满足,并精确追踪该插件在 Context 上创建的所有待释放资源,以便在卸载时执行资源清理和释放工作。
1. Fiber的创建
Cordis会在如下两种场景中创建作为生命周期管理器的Fiber对象。一种是直接调用构造函数创建根Context的时候,创建的Fiber将作为Context的fiber属性,该对象将作为当前Context的生命周期管理器。
export class Context {
constructor() {
...
this.fiber = new Fiber(self, {}, Object.create(null), null, () => [])
...
}
}
针对Fiber的另一个创建场景发生在调用RegistryService的plugin方法进行插件注册的时候。实际上该方法返回的就是一个Fiber对象,该对象将作为插件专属的生命周期管理器。
export class RegistryService {
plugin(plugin: Plugin, config?: any, getOuterStack = buildOuterStack()) {
...
const fiber = new Fiber(this.ctx, config, Inject.resolve(plugin.inject), runtime, getOuterStack)
const wrapped = Object.create(fiber) as Fiber & PromiseLike<Fiber>
wrapped.then = (onFulfilled, onRejected) => {
return fiber.await().then(onFulfilled, onRejected)
}
return wrapped
}
}
2. 执行操作收集待释放资源
Fiber提供了如下两个核心的重载方法effect,它执行一项指定操作,并通过方法返回的函数恢复因操作执行导致的副作用,具体来说,我们可以利用返回的函数:
- 释放操作执行过程中分配的资源;
- 释放针对全局或者外部兑现的引用,比如注册的事件处理函数;
- 复位操作修改的一些配置选项。
执行的操作通过execute参数传入的函数表示,它可利用返回一个SyncEffect<any>或者AsyncEffect<any>类型的对象,其中SyncEffect<T>和AsyncEffect<T>分别是针对Disposable<T>与Iterable<Disposable<T>, void, void>以及Promise<Disposable<T>>与AsyncIterable<Disposable<T>, void, void>的联合类型。我们在调用这两个方法的时候还可以指定一个可选的诊断标签。
export class Fiber {
effect(execute: () => SyncEffect, label?: string): Disposable<Promise<void>>
effect(execute: () => Effect, label?: string): AsyncDisposable<Promise<void>>
}
export type Effect<T = any> =
| SyncEffect<T>
| AsyncEffect<T>
type SyncEffect<T = any> =
| Disposable<T>
| Iterable<Disposable<T>, void, void>
type AsyncEffect<T = any> =
| Promise<Disposable<T>>
| AsyncIterable<Disposable<T>, void, void>
type Disposable<T = any> = () => T
interface AsyncDisposable<T extends Awaitable<void> = Awaitable<void>> extends PromiseLike<() => T> {
(): T
}
两个重载的effect方法分别返回一个通过Disposable<Promise<void>>和AsyncDisposable<Promise<void>>表示的函数,我们可以直接执行它们完成清理工作。在如下的演示程序中,我们调用根Context的fiber的effect方法利用创建的timer以1秒为间隔输出当前时间。作为execute参数的函数返回另一个用来清理计时器的函数。在得到effect方法返回的Disposable类型的函数后,我们在5秒后执行它实现对计时器的清理。
import { Context } from '@deepseek-ai/cordis'
const context = new Context();
const disposable = context.fiber.effect(()=>{
const timer = setInterval(()=>console.log(new Date()),1000);
return ()=> {
console.log("Clear the timer...")
clearInterval(timer);
}
});
setTimeout(()=>disposable(), 5000);
process.stdin.resume();
输出:
2026-08-25T15:45:04.915Z
2026-08-25T15:45:05.926Z
2026-08-25T15:45:06.933Z
2026-08-25T15:45:07.942Z
Clear the timer...
3. 调用Fiber的dispose方法执行清理工作
effect方法返回的Disposable对象还会被Fiber对象收集起来,我们可以如下的方式调用Fiber对象的dispose方法也能完成最终的清理工作:
import { Context } from '@deepseek-ai/cordis'
const context = new Context();
context.fiber.effect(()=>{
const timer = setInterval(()=>console.log(new Date()),1000);
return ()=> {
console.log("Clear the timer...")
clearInterval(timer);
}
});
setTimeout(()=>{
context.fiber.dispose();
}, 5000);
process.stdin.resume();
输出:
2026-08-25T15:55:06.461Z
2026-08-25T15:55:07.462Z
2026-08-25T15:55:08.477Z
2026-08-25T15:55:09.487Z
Clear the timer...
4. 重启插件
Fiber提供了用于重启的restart方法,我们可以利用它重启注册的插件。以如下的演示程序为例,我们利用根Context的RegistryService注册了一个插件,该插件以一秒为间隔输出从1开始的自增数字,并在4秒之后对得到的Fiber对象进行重启。为了真正终止之前正在执行的插件,我们在重启Fiber时必须释放之前创建的计时器。
import { Context } from '@deepseek-ai/cordis'
const context = new Context();
var timeout: NodeJS.Timeout
const fiber = context.registry.plugin(ctx =>{
let counter = 1;
const timer = setInterval(()=>console.log(counter ++),1000);
timeout = setTimeout(()=>{
console.log("Restart plugin...")
timer.close();
timeout?.close();
fiber.restart();
}, 4000);
});
输出:
1
2
3
Restart plugin...
1
2
3
Restart plugin...
1
2
...
由于Fiber的restart方法在执行重启过程中对当前对象实施卸载(Unload), 其中会涉及针对dispose方法的调用。我们在DeepSeek Harness插件内核-01:DeepSeek Harness三种插件形式中就已经说过:插件函数返回的函数会在Fiber的dispose方法被调用时作为清理插件的操作被执行。所以上面演示程序中针对计时器的关闭最好采用如下的形式来完成。
import { Context } from '@deepseek-ai/cordis'
const context = new Context();
const fiber = context.registry.plugin(ctx =>{
let counter = 1;
const timer = setInterval(()=>console.log(counter ++),1000);
return ()=>timer.close();
});
setInterval(()=>{
console.log("Restart plugin...")
fiber.restart();
}, 4000);
process.stdin.resume();
输出:
1
2
3
Restart plugin...
1
2
3
Restart plugin...
1
2
...
5. 修改插件配置
如果需要动态修改插件的配置,我们可以调用插件绑定Fiber对象的update方法。以如下的演示程序为例,我们利用根Context的RegistryService注册了一个插件,该插件会以一秒的间隔输出一条问候语。问候语的时间部分来源于配置对象的timeOfDay字段。在完成插件注册的4秒和8秒后,我们分别调用Fiber对象的update方法修改了配置。配置修改之后,如果当前Fiber对象人处于激活状态,它将会重启,重启后将应用新的配置。
import { Context } from '@deepseek-ai/cordis'
const context = new Context();
const fiber = context.registry.plugin((ctx, config) =>{
const timeOfDay = (config as {timeOfDay:string})?.timeOfDay??"morning";
const timeout = setInterval(()=> console.log(`Good ${timeOfDay}!`), 1000);
return ()=>timeout.close();
});
setTimeout(()=>{
fiber.update({timeOfDay: "afternoon"})
}, 4000);
setTimeout(()=>{
fiber.update({timeOfDay: "evening"})
}, 8000);
输出:
Good morning!
Good morning!
Good morning!
Good afternoon!
Good afternoon!
Good afternoon!
Good evening!
Good evening!
...

346

被折叠的 条评论
为什么被折叠?



