JavaScript代码大量连续if校验如何重构?handleRefresh实例求优化建议
handleRefresh 函数校验逻辑重构建议
现存问题
- 校验逻辑与业务逻辑耦合,主流程被大量判断语句打断,可读性差
- 重复IO:循环内重复读取
postr.toml,存在无意义性能损耗 - 嵌套层级深:最多4层嵌套,后期修改维护成本高
- 校验逻辑分散:同类规则散落在代码不同位置,修改规则需要改动多处
- 隐含逻辑bug:
isValidFrontMatter返回true时反而抛出frontMatter不合法的错误,逻辑反向 async forEach回调的错误无法被外层统一捕获,异常处理能力弱
重构方案
1. 抽离公共校验工具,消除重复判断模板
把通用的「条件校验+报错/告警+逻辑中断」逻辑封装为工具函数,一行完成校验,消除冗余的if模板代码:
// 校验不满足则调用fail并抛出错误中断执行 function assert(condition: boolean, errMsg: string) { if (!condition) { fail(errMsg); throw new Error(errMsg); } } // 条件满足则输出警告 function warnIf(condition: boolean, warnMsg: string) { if (condition) { console.warn(warnMsg); } }
2. 全局配置提前校验&缓存,避免循环内重复操作
postr.toml的读取、校验逻辑提前到函数最开始执行,只执行一次,不需要在遍历适配器时重复读取:
// 函数开头直接完成配置校验缓存 assert(existsSync('postr.toml'), 'not a postr directory'); const globalConfig = parseToml(await read('postr.toml')); const adapterPlugins = globalConfig.adapterPlugins ?? {}; assert(isObject(adapterPlugins), 'adapterPlugins in the configuration is not an object');
3. 按职责拆分函数,校验逻辑内聚
把原函数拆分为三个独立模块,每个模块仅处理对应环节的校验,主流程只负责按顺序调用:
- 全局配置模块:处理配置文件的读取、校验、缓存
- 单文章处理模块:处理文章目录合法性、frontMatter合法性、adapters格式校验
- 适配器执行模块:处理适配器配置合法性、能力校验、执行逻辑
4. 替换async forEach为for of,统一异常处理
改用for of遍历文章和适配器,配合assert抛出的错误,外层统一catch后跳过当前异常项,不需要编写大量continue/return语句。
重构后代码示例
export async function handleRefresh() { // 1. 全局配置校验&缓存 assert(existsSync('postr.toml'), 'not a postr directory'); const globalConfig = parseToml(await read('postr.toml')); const adapterPlugins = globalConfig.adapterPlugins ?? {}; assert(isObject(adapterPlugins), 'adapterPlugins in the configuration is not an object'); const posts = expandGlob('posts/*'); for await (const post of posts) { try { // 2. 单文章前置校验 warnIf(!post.isDirectory, 'warning: non-folder found in posts directory'); if (!post.isDirectory) continue; const postMdContent = await read(`${post.path}/post.md`); let { parsedFrontMatter, contents } = extractFrontMatter(postMdContent); assert(!!parsedFrontMatter, `no frontmatter for ${post.path}`); const adapters = parsedFrontMatter.adapters ?? []; assert(Array.isArray(adapters), 'adapters is not an array'); assert(!isValidFrontMatter(parsedFrontMatter), 'frontmatter is not valid'); // 修正原逻辑反向问题 // 3. 遍历适配器执行 for (const adapter of adapters) { try { const adapterConfig = adapterPlugins[adapter]; if (!adapterConfig) { console.warn(`warn: an adapter was set but the corresponding plugin was not configured in \`postr.toml\`. Skipping`); continue; } assert('path' in adapterConfig, `adapter ${adapter} does not have a path`); const module = await import(adapterConfig.path); const action = getActionForPost(parsedFrontMatter); if (!module[action]) { console.warn(`Adapter ${adapter} does not support action \`${action}\``); continue; } await module[action](contents, parsedFrontMatter, adapterConfig.config, { updateFrontMatter(newData: {[x: string]: any}) { parsedFrontMatter = Object.assign(parsedFrontMatter, newData); }, mapID(remote: string | number) { addMapping(parsedFrontMatter.id as string, remote.toString(), adapter); } }); writeFinalContents(parsedFrontMatter, contents, post.path); } catch (e) { fail(`could not run adapter because of ${e.name}: ${e.message}`); } } } catch (e) { // 单文章校验失败自动跳过,不需要手动写continue continue; } } }
重构收益
- 代码行数减少30%以上,主流程逻辑清晰,没有冗余嵌套
- 校验规则统一维护,后续修改规则只需要改对应位置或者assert工具即可
- 消除了重复IO和逻辑反向的隐性bug
- 异常处理统一,不需要散落在各处写中断逻辑
内容的提问来源于stack exchange,提问作者Siddharth Shyniben
相关产品推荐
相关产品推荐

