错误的分类与处置
Makoo 不按「出了什么问题」给错误分类,而是按在哪里出现、由谁处理来分类。知道一个错误属于哪一类,就知道该去哪里找它、它会不会影响功能运行。
完整错误码见 错误 API。
五类错误
| 类别 | 何时出现 | 你在哪里看到 | 对功能的影响 |
|---|---|---|---|
| 配置错误 | useAdapter()、apply() 时 | 同步抛出 | 整批都不启动 |
| 控制错误 | 调用命令或查询入口时 | 同步抛出 | 这次调用无效 |
| 执行失败 | 运行过程中 | lastError、控制台 | 结束这一轮,状态 failed |
| 清理失败 | 收尾过程中 | lastError、控制台、stop() / remove() 的拒绝 | 注入被关闭 |
| 回调异常 | 你的回调抛错时 | 控制台,部分写入 lastError | 不影响 |
配置错误:开始前就报告
名称重复、附属监听设置了 reinject、组件选择的适配器未注册、传给 apply() 的数组为空,或其中某一项不是组件、Each 注入或监听声明,都会在 apply() 时同步抛出,并且整批声明都不会启动。重复注册同名适配器则会在 useAdapter() 时同步抛出。完整列表见配置错误码。
这样做是为了避免一批功能启动到一半才发现另一半配置有误,留下难以解释的部分状态。配置错误通常在开发阶段第一次打开页面时就会出现,直接修改代码即可。
控制错误:这次调用不被允许
对不存在的名称调用 core.injection() 或 core.listener(),启动已经移除的注入,在 Makoo 挂载的组件之外调用 useMakooComponent(),都属于控制错误。它们同步抛出,说明调用方的假设与当前状态不符。
执行失败:这一轮结束,可以再来
执行失败发生在启动之后,例如等不到目标、适配器挂载抛错、目标失效且不允许恢复。因为它发生在后台,apply() 早已返回,所以 Core 不会抛出,而是:
- 把错误写入状态入口的
lastError; - 用
console.error打印错误对象; - 收尾成功后把状态设为
failed。
执行失败不会自动重试,但收尾成功后可以再次 start()。例如目标是用户操作后才出现的,可以在合适的时机手动启动。如果收尾本身也失败(例如适配器挂载失败后无法释放干净),就按下面的清理失败处理。
清理失败:无法确认干净,就不再继续
收尾时,卸载组件、移除容器或解绑事件失败,Core 就无法确认页面上没有残留。此时注入被关闭:状态为 failed,lastError 是汇总所有错误的 MakooAggregateError,名称继续占用,之后的 start() 抛出 MAKOO_INJECTION_CLOSED。
如果清理是由 stop() 或 remove() 触发的,它们返回的 Promise 也会以同一个错误对象拒绝。dispose() 则以 MAKOO_INSTANCE_CLEANUP_FAILED 拒绝,各注入的清理错误在它的 errors 中。原因见为什么清理失败会关闭注入。
回调异常:只报告,不扩大
Core 会调用不属于它的代码:事件处理函数、状态订阅者、observe() 回调。这些代码同步抛错时,Core 只报告,不把它当成执行失败:事件绑定保持,组件不卸载,状态不变,其他订阅者照常收到通知。
理由是这类错误通常只影响这一次处理。如果因为一次点击的处理函数出错就卸载组件、解绑全部事件,局部问题就会变成整个功能不可用。
报告方式略有不同:
| 来源 | 控制台 | lastError | 实例观察 |
|---|---|---|---|
| 独立监听的事件处理函数 | 打印 | 写入 | 不通知 |
| 附属监听的事件处理函数 | 打印 | 不写入 | 不通知 |
| 状态订阅者 | 打印 | 不写入 | diagnostic |
observe() 回调 | 打印 | 不适用 | diagnostic(不带 name) |
事件处理函数返回被拒绝的 Promise 时也按上表报告。状态订阅者和 observe() 回调返回的 Promise 不会被等待,其中的拒绝 Makoo 不会接住。
状态订阅者的异常不写入 lastError,是因为订阅者往往在状态变为 failed 时被调用;如果写入,就会覆盖真正的失败原因。
读懂一个错误
Makoo 抛出或报告的错误都是 MakooError,关键字段有三个:
message:Makoo 在做什么、对象是谁,例如Failed to mount "panel"。code:错误种类,在代码中用MakooErrorCode常量比较。cause:Makoo 接住的原始错误,例如你的组件在setup中抛出的错误。
Makoo 只在接住别人的错误时包装一层,超时、目标失效这类自己判定的问题直接报告、不带 cause。在浏览器控制台展开错误,沿 cause 往下就能找到原始代码行。汇总错误(MakooAggregateError)另有 errors,列出每一个子错误,不会丢失;其中适配器给出的清理错误会原样保留,不一定是 MakooError。
Makoo 没有自己的日志系统,控制台是唯一的输出位置。需要在代码中处理错误时,读取状态入口的 lastError,或在状态变为 failed 时做出反应,见常见问题。