编码母题 / P1 / 前端与专项架构 / 03
插件注册与生命周期系统
先读清题意与边界,独立尝试,再展开思路并对照可运行的参考代码。
重要程度高价值难度困难预计完成45–60 分钟
题目是什么
插件声明名称、依赖和 setup;按依赖顺序启动。任一插件失败时按已成功启动的逆序回滚,正常停止也逆序卸载。
输入输出示例
B 依赖 A,若 B 的 setup 失败,应调用 A 已登记的 teardown;B 未成功返回 teardown,不得假装它已完成卸载。
练习起手式与验证
复制代码后补全空方法,再运行下方三组验证;复杂题所需的辅助类型与输入准备一并放在代码块中。
/*
* 题目:插件注册与生命周期系统
* 要求:插件声明名称、依赖和 setup;按依赖顺序启动。任一插件失败时按已成功启动的逆序回滚,
* 正常停止也逆序卸载。
* 示例:B 依赖 A,若 B 的 setup 失败,应调用 A 已登记的 teardown;B
* 未成功返回 teardown,不得假装它已完成卸载。
*/
class PluginSystem {
constructor() {}
register(plugin) {}
order() {}
async start(context = {}) {}
async rollback() {}
async stop() {}
}
(async () => {
const order = [];
const system = new PluginSystem()
.register({
name: "db",
setup: async () => {
order.push("db+");
return () => order.push("db-");
},
})
.register({
name: "api",
dependencies: ["db"],
setup: async () => {
order.push("api+");
return () => order.push("api-");
},
});
await system.start();
await system.stop();
console.log(order); // 预期:["db+", "api+", "api-", "db-"]
const failed = new PluginSystem()
.register({ name: "a", setup: () => () => order.push("a-") })
.register({
name: "b",
dependencies: ["a"],
setup: () => {
throw new Error("boom");
},
});
console.log(await Promise.resolve(failed.start()).then(() => false, () => true)); // 预期:true(拒绝)
console.log(failed.status); // 预期:"stopped"
})().catch(console.error);
/*
* 题目:插件注册与生命周期系统
* 要求:插件声明名称、依赖和 setup;按依赖顺序启动。任一插件失败时按已成功启动的逆序回滚,
* 正常停止也逆序卸载。
* 示例:B 依赖 A,若 B 的 setup 失败,应调用 A 已登记的 teardown;B
* 未成功返回 teardown,不得假装它已完成卸载。
*/
class PluginSystem {
constructor() {}
register(plugin) {}
order() {}
async start(context = {}) {}
async rollback() {}
async stop() {}
}
(async () => {
const order = [];
const system = new PluginSystem()
.register({
name: "db",
setup: async () => {
order.push("db+");
return () => order.push("db-");
},
})
.register({
name: "api",
dependencies: ["db"],
setup: async () => {
order.push("api+");
return () => order.push("api-");
},
});
await system.start();
await system.stop();
console.log(order); // 预期:["db+", "api+", "api-", "db-"]
const failed = new PluginSystem()
.register({ name: "a", setup: () => () => order.push("a-") })
.register({
name: "b",
dependencies: ["a"],
setup: () => {
throw new Error("boom");
},
});
console.log(await Promise.resolve(failed.start()).then(() => false, () => true)); // 预期:true(拒绝)
console.log(failed.status); // 预期:"stopped"
})().catch(console.error);解题思路
展开思路、不变量与常见边界
关键切入点:拓扑排序保证启动顺序,成功 setup 的 teardown 构成逆序释放栈。
核心模型:注册阶段构建静态图,启动阶段形成资源栈。逆序释放保证依赖者先于 被依赖者销毁。不变量:running 时所有依赖已先启动;teardown 栈只包含成功项; 失败后回到 stopped。迁移:前端平台、Agent Tool、构建插件和服务容器。
参考代码与验证
“关键机制”注释用于解释实现,下载链接提供未经页面注释修改的原始 JS 文件。
// 关键机制:拓扑排序保证启动顺序,成功 setup 的 teardown 构成逆序释放栈。
class PluginSystem {
constructor() {
this.plugins = new Map();
this.teardowns = [];
this.status = "stopped";
}
register(plugin) {
if (this.status !== "stopped")
throw new Error("cannot register while running");
if (
!plugin ||
typeof plugin.name !== "string" ||
typeof plugin.setup !== "function"
)
throw new TypeError("invalid plugin");
if (this.plugins.has(plugin.name))
throw new Error(`duplicate plugin: ${plugin.name}`);
this.plugins.set(plugin.name, plugin);
return this;
}
order() {
const visiting = new Set();
const visited = new Set();
const result = [];
const visit = (name) => {
if (visited.has(name)) return;
if (visiting.has(name)) throw new Error("plugin dependency cycle");
const plugin = this.plugins.get(name);
if (!plugin) throw new Error(`missing plugin: ${name}`);
visiting.add(name);
for (const dep of plugin.dependencies || []) visit(dep);
visiting.delete(name);
visited.add(name);
result.push(plugin);
};
for (const name of this.plugins.keys()) visit(name);
return result;
}
async start(context = {}) {
if (this.status !== "stopped") throw new Error("already started");
this.status = "starting";
try {
for (const plugin of this.order()) {
const teardown = await plugin.setup(context);
this.teardowns.push({
name: plugin.name,
teardown: typeof teardown === "function" ? teardown : async () => {},
});
}
this.status = "running";
} catch (error) {
await this.rollback();
throw error;
}
}
async rollback() {
const errors = [];
while (this.teardowns.length) {
const item = this.teardowns.pop();
try {
await item.teardown();
} catch (e) {
errors.push(e);
}
}
this.status = "stopped";
if (errors.length)
throw new AggregateError(errors, "plugin teardown failed");
}
async stop() {
if (this.status !== "running") throw new Error("not running");
await this.rollback();
}
}
// 三组以上可直接运行的验证;异步题使用独立执行块。
(async () => {
const order = [];
const system = new PluginSystem()
.register({
name: "db",
setup: async () => {
order.push("db+");
return () => order.push("db-");
},
})
.register({
name: "api",
dependencies: ["db"],
setup: async () => {
order.push("api+");
return () => order.push("api-");
},
});
await system.start();
await system.stop();
console.log(order); // 预期:["db+", "api+", "api-", "db-"]
const failed = new PluginSystem()
.register({ name: "a", setup: () => () => order.push("a-") })
.register({
name: "b",
dependencies: ["a"],
setup: () => {
throw new Error("boom");
},
});
console.log(await Promise.resolve(failed.start()).then(() => false, () => true)); // 预期:true(拒绝)
console.log(failed.status); // 预期:"stopped"
})().catch(console.error);
// 关键机制:拓扑排序保证启动顺序,成功 setup 的 teardown 构成逆序释放栈。
class PluginSystem {
constructor() {
this.plugins = new Map();
this.teardowns = [];
this.status = "stopped";
}
register(plugin) {
if (this.status !== "stopped")
throw new Error("cannot register while running");
if (
!plugin ||
typeof plugin.name !== "string" ||
typeof plugin.setup !== "function"
)
throw new TypeError("invalid plugin");
if (this.plugins.has(plugin.name))
throw new Error(`duplicate plugin: ${plugin.name}`);
this.plugins.set(plugin.name, plugin);
return this;
}
order() {
const visiting = new Set();
const visited = new Set();
const result = [];
const visit = (name) => {
if (visited.has(name)) return;
if (visiting.has(name)) throw new Error("plugin dependency cycle");
const plugin = this.plugins.get(name);
if (!plugin) throw new Error(`missing plugin: ${name}`);
visiting.add(name);
for (const dep of plugin.dependencies || []) visit(dep);
visiting.delete(name);
visited.add(name);
result.push(plugin);
};
for (const name of this.plugins.keys()) visit(name);
return result;
}
async start(context = {}) {
if (this.status !== "stopped") throw new Error("already started");
this.status = "starting";
try {
for (const plugin of this.order()) {
const teardown = await plugin.setup(context);
this.teardowns.push({
name: plugin.name,
teardown: typeof teardown === "function" ? teardown : async () => {},
});
}
this.status = "running";
} catch (error) {
await this.rollback();
throw error;
}
}
async rollback() {
const errors = [];
while (this.teardowns.length) {
const item = this.teardowns.pop();
try {
await item.teardown();
} catch (e) {
errors.push(e);
}
}
this.status = "stopped";
if (errors.length)
throw new AggregateError(errors, "plugin teardown failed");
}
async stop() {
if (this.status !== "running") throw new Error("not running");
await this.rollback();
}
}
// 三组以上可直接运行的验证;异步题使用独立执行块。
(async () => {
const order = [];
const system = new PluginSystem()
.register({
name: "db",
setup: async () => {
order.push("db+");
return () => order.push("db-");
},
})
.register({
name: "api",
dependencies: ["db"],
setup: async () => {
order.push("api+");
return () => order.push("api-");
},
});
await system.start();
await system.stop();
console.log(order); // 预期:["db+", "api+", "api-", "db-"]
const failed = new PluginSystem()
.register({ name: "a", setup: () => () => order.push("a-") })
.register({
name: "b",
dependencies: ["a"],
setup: () => {
throw new Error("boom");
},
});
console.log(await Promise.resolve(failed.start()).then(() => false, () => true)); // 预期:true(拒绝)
console.log(failed.status); // 预期:"stopped"
})().catch(console.error);
复杂度分析
时间复杂度:O(V+E),不计 setup/teardown。拓扑访问每个插件和依赖一次,启动与回滚逐插件处理。
空间复杂度:O(V+E)。依赖遍历状态和逆序清理栈与插件图规模同阶。