企业级报表引擎需在语法解析、上下文绑定、值求值、类型转换四环节分层设防异步公式解析失败,通过try-catch嵌入生命周期实现故障可控、降级可配、日志可溯。

公式解析阶段:捕获语法与上下文错误
用户输入的公式(如 data?.sales?.[currentYear]?.revenue ?? 0)可能含非法字符、未声明变量或不支持的运算符。直接交给 JS 引擎 eval 或 Function 构造器会抛出 SyntaxError 或 ReferenceError。
应在解析入口统一包裹:
- 用
try { new Function('data', formula) }替代eval,避免作用域污染;失败时捕获SyntaxError,返回预设占位符(如 "公式无效")并记录原始表达式供审计 - 对变量引用(如
user.profile.name)做静态路径校验:提取所有访问路径,提前检查数据模型是否包含对应字段层级;缺失时抛出自定义FieldPathNotFoundError,而非等到运行时报TypeError
异步求值阶段:隔离 Promise 拒绝与超时中断
公式中调用 asyncGetData() 或 fetchMetric("pv") 类函数时,失败形态多样:网络超时、4xx/5xx 响应、空响应体、非 JSON 格式。单纯 .catch() 会丢失堆栈,且无法区分重试型错误与终态错误。
- 所有异步调用必须用
try { const res = await promise } catch (e)显式包裹,禁止链式.then().catch()—— 后者无法触发上层重试逻辑 - 对超时类错误(
AbortError、TimeoutError)启用指数退避重试;对 401/403 等认证失败,直接降级为 "无权限查看" 并跳过重试 - 设置全局超时阈值(如 8s),用
AbortController主动中断挂起请求,防止阻塞后续字段渲染
类型安全阶段:防御性转换与空值归一化
后端返回的 "null" 字符串、空数组 []、数字 0、布尔 false 在公式中常被误判为“有效值”,导致 sum([0, null, 5]) 得到 5 而非 5(正确应忽略 null)。
- 在值注入公式上下文前,执行统一空值规约:将
null、undefined、"null"、""归一为undefined;将空数组[]视为null(除非明确标注“空集合合法”) - 公式求值后,对结果做类型守卫:若公式声明返回
number,但实际得"123",则尝试Number()转换;失败时记录警告日志,返回 "数据格式异常" 而非NaN
兜底与可观测阶段:分级降级 + 全链路追踪
单点 try-catch 只解决“不崩”,企业级需要“可知、可配、可恢复”。失败不应静默吞掉,而要暴露给运维和业务方。
- 按失败等级设定降级策略:语法错误 → 显示公式原文+红标;网络失败 → 显示上次成功值+刷新图标;类型错误 → 显示“格式异常”+字段名
- 每个公式执行生成唯一
formula_id,与报表report_id、用户session_id关联;日志中记录:公式文本、入参快照、错误类型、堆栈片段(脱敏)、重试次数、最终降级值 - 暴露指标:每分钟“公式失败率”、“平均重试延迟”、“高频失败字段TOP5”,接入告警系统,当某字段连续 5 分钟失败率 > 30%,自动通知数据接口负责人

















