mcpField
C
control-flow

control-flow

github.com/epicenterhq开发4.8k75 次安装2 周前更新
代码仓库

关于 control-flow

control-flow 是帮助开发者重构复杂控制流的编程技能,强调按自然的判断顺序组织代码。它以提前返回、互斥条件和清晰命名减少嵌套,并将失败处理与正常路径分开。

主要功能

  1. 01优先处理可提前返回的情况
  2. 02用互斥条件描述当前状态
  3. 03将主要操作集中在正常路径
  4. 04使用自然语言风格的布尔变量名
  5. 05把错误边界与后续逻辑分开

适用场景

  1. 01简化含重复逻辑的嵌套条件
  2. 02整理带多个失败点的处理器
  3. 03改写仅有单一风险调用的 try-catch

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/epicenterhq/epicenter --skill control-flow

安装后在 Claude Code 中输入/control-flow确认已加载。

详细介绍

易读的控制流

重构复杂控制流时,应贴合人类自然的推理模式:

相关技能:有关分支折叠和调用方证据,请参阅重构方法。

核心模式

  1. 先提出人的问题:“能用已有的东西吗?”→ 在符合预期的路径上提前返回
  2. 评估情况:“我目前处于什么状态?需要做什么?”→ 条件清晰且互斥
  3. 采取行动:“获取我需要的东西”→ 在末尾集中处理逻辑
  4. 使用自然语言变量:isUsingNavigator、isUsingLocalTranscription、needsOldFileCleanup:名称读起来就像人的思路
  5. 避免人为构造:不要使用与人实际思考问题的方式不符的嵌套条件

将这种结构:包含重复逻辑的嵌套条件 改为这种结构:贴合人类决策过程的线性流程

示例:使用自然语言变量提前返回

// From apps/whispering/src/routes/(app)/_layout-utils/check-ffmpeg.ts

export async function checkFfmpegRecordingMethodCompatibility() {
	if (!window.__TAURI_INTERNALS__) return;

	// Only check if FFmpeg recording method is selected
	if (settings.value['recording.method'] !== 'ffmpeg') return;

	const { data: ffmpegInstalled } =
		await rpc.ffmpeg.checkFfmpegInstalled.ensure();
	if (ffmpegInstalled) return; // FFmpeg is installed, all good

	// FFmpeg recording method selected but not installed
	toast.warning('FFmpeg Required for FFmpeg Recording Method', {
		// ... toast content
	});
}

示例:自然语言布尔值

// From apps/whispering/src/routes/(app)/_layout-utils/check-ffmpeg.ts

const isUsingNavigator = settings.value['recording.method'] === 'navigator';
const isUsingLocalTranscription =
	settings.value['transcription.selectedTranscriptionService'] ===
		'whispercpp' ||
	settings.value['transcription.selectedTranscriptionService'] === 'parakeet';

return isUsingNavigator && isUsingLocalTranscription && !isFFmpegInstalled;

示例:带注释的清理检查

// From packages/epicenter/src/indexes/markdown/markdown-index.ts

/**
 * This is checking if there's an old filename AND if it's different
 * from the new one. It's essentially checking: "has the filename
 * changed?" and "do we need to clean up the old file?"
 */
const needsOldFileCleanup = oldFilename && oldFilename !== filename;
if (needsOldFileCleanup) {
	const oldFilePath = path.join(tableConfig.directory, oldFilename);
	await deleteMarkdownFile({ filePath: oldFilePath });
	tracking[table.name]!.deleteByFilename({ filename: oldFilename });
}

示例:将 try-catch 线性化为守卫条件 + 符合预期的路径

try-catch 块会形成嵌套的双分支结构:try 主体和 catch 主体。如果 try 中只有一个调用可能实际抛出异常,就可以将 try-catch 替换为带守卫条件的调用 + 提前返回,让代码自上而下地阅读。

之前(嵌套结构,混合抛出与返回):

async ({ body, status }) => {
	const adapter = createAdapter(body.provider);

	try {
		const stream = chat({ adapter, messages: body.messages });
		return toServerSentEventsResponse(stream);
	} catch (error) {
		if (error instanceof Error && error.name === 'AbortError') {
			throw status(499, 'Client closed request');
		}
		const message = error instanceof Error ? error.message : 'Unknown error';
		throw status('Bad Gateway', `Provider error: ${message}`);
	}
};

之后(线性结构,返回方式一致):

async ({ body, status }) => {
	const adapter = createAdapter(body.provider);

	const { data: stream, error: chatError } = trySync({
		try: () => chat({ adapter, messages: body.messages }),
		catch: (e) => Err(e instanceof Error ? e : new Error(String(e))),
	});

	if (chatError) {
		if (chatError.name === 'AbortError') {
			return status(499, 'Client closed request');
		}
		return status('Bad Gateway', `Provider error: ${chatError.message}`);
	}

	return toServerSentEventsResponse(stream);
};

这种转换遵循相同的人类推理模式:

  1. 尝试执行有风险的操作:只对可能失败的部分使用封装
  2. 检查是否失败:使用适当的错误提前返回
  3. 继续执行符合预期的路径:函数的其余部分默认操作成功

这样可以消除嵌套结构,统一 return 与 throw 的使用方式,并将错误边界与后续安全代码分开。

示例:处理器中的顺序守卫条件

处理器有多个可能失败的地方时,每个守卫条件都遵循相同的模式:执行操作、检查结果,然后提前返回或继续。

async ({ body, status }) => {
	// Guard 1: validate input
	if (!isSupportedProvider(body.provider)) {
		return status('Bad Request', `Unsupported provider: ${body.provider}`);
	}

	// Guard 2: resolve dependency
	const apiKey = resolveApiKey(body.provider, headers['x-api-key']);
	if (!apiKey) {
		return status('Unauthorized', 'Missing API key');
	}

	// Guard 3: risky operation
	const { data: stream, error } = trySync({
		try: () => chat({ adapter: createAdapter(body.provider, apiKey) }),
		catch: (e) => Err(e instanceof Error ? e : new Error(String(e))),
	});
	if (error) return status('Bad Gateway', error.message);

	// Happy path: all guards passed
	return toServerSentEventsResponse(stream);
};

每个守卫条件的形式都相同:检查 → 失败时提前返回。符合预期的路径在底部逐步汇集。自上而下阅读时,你会先看到函数可能失败的所有方式,然后才看到成功情形。

评论

支持 Markdown