资讯动态

Angular NG0991 错误排查:rxResource / httpResource 在产出值之前完成(Resource completed before producing a value)

发布时间:2026/9/10 2:44:59 来源:尧图企业网站定制
Angular NG0991 错误排查rxResource / httpResource 在产出值之前完成Resource completed before producing a value【免费下载链接】angularDeliver web apps with confidence 项目地址: https://gitcode.com/GitHub_Trending/an/angular本篇指南围绕 Angular 官方错误NG0991RESOURCE_COMPLETED_BEFORE_PRODUCING_VALUE错误文案 “Resource completed before producing a value”展开说明它在rxResource与httpResource两种基于 Observable 的异步资源 API 下分别由什么触发、错误实际在何时抛出并给出“放行错误”“提供回退值”两类修复方案与读取侧的防御性写法。读完你不仅能定位此类报错的真正根源还能理解 Angular 资源加载管线中stream完成回调的底层判定逻辑。错误是什么资源必须以“值”或“错误”收场rxResource与httpResource都是把一段异步逻辑包装成响应式信号Signal的 Angular 资源 API。一个资源永远需要以某种结果结束一个加载成功的值或一个说明失败原因的错误。而 NG0991 的场景是底层 Observable既没有发出任何值也没有发出任何错误就直接静默完成complete了。Angular 此时手里什么都没有不知道该把资源的value()设为多少也不知道该把error()设为哪个错误于是抛出该运行时错误。该错误在官方错误总表中登记为NG0991见 adev/src/content/reference/errors/overview.md其错误码常量定义在 packages/core/src/errors.ts 的resource() API errors分组中// resource() API errors MUST_PROVIDE_STREAM_OPTION 990, RESOURCE_COMPLETED_BEFORE_PRODUCING_VALUE -991, INVALID_RESOURCE_CREATION_IN_PARAMS 992,错误码-991的命名直译为“资源在产出值之前完成”与报错文案一一对应。底层判定逻辑complete 回调里的hasResolved检查从源码看这个错误的产生点位于rxResource内部对stream的订阅逻辑中见 packages/core/rxjs-interop/src/rx_resource.tssub streamFn(params).subscribe({ next: (value) send({value}), error: (error: unknown) { send({error: encapsulateResourceError(error)}); params.abortSignal.removeEventListener(abort, onAbort); }, complete: () { if (!hasResolved) { send({ error: new ɵRuntimeError( ɵRuntimeErrorCode.RESOURCE_COMPLETED_BEFORE_PRODUCING_VALUE, ngDevMode Resource completed before producing a value, ), }); } params.abortSignal.removeEventListener(abort, onAbort); }, });关键点在于next收到值后通过send({value})记录值并调用resolveOnce()把hasResolved置为trueerror收到错误后同样send({error})并标记已产出结果complete触发时若hasResolved仍为false说明整个流“空手而归”此时才会构造并下发NG0991错误。也就是说只要流在完成前发出过至少一个值或一个错误hasResolved就为真完成时不会触发该错误。这也是下文所有修复方案的统一原则保证stream返回的 Observable 在 complete 之前至少 emit 一个值或抛出一个错误。另外可以看到描述文案通过ngDevMode Resource completed before producing a value传入这与 Angular 一贯的做法一致——开发模式下ngDevMode为真错误携带完整描述生产构建下该参数会被 tree-shake 掉参见 packages/core/src/errors.ts 中RuntimeError对 message 参数的说明。官方测试 packages/core/rxjs-interop/test/rx_resource_spec.ts 也直接验证了这一行为stream: () EMPTY时res.status()变为error且res.value()抛出/Resource completed before producing a value/。使用httpResource时问题几乎总在拦截器层httpResource实现见 packages/common/http/src/resource.ts并不像rxResource那样把 Observable 交给你自己编写——它内部通过HttpClient发起 HTTP 请求响应返回后流即完成。因此如果你从httpResource身上看到 NG0991请求本身通常不是问题而是“坐在请求前面的某个东西”把响应吞掉了。最常见的元凶是HttpInterceptor拦截器捕获了错误却没有重新发出任何东西。下面的写法与后面rxResource章节里catchError(() EMPTY)是同一个错误只是它发生在拦截器里而不是你自己的流代码中Injectable() class MyInterceptor implements HttpInterceptor { intercept(req: HttpRequestunknown, next: HttpHandler) { return next.handle(req).pipe( // BAD: same problem as catchError(() EMPTY) in a stream — // the request completes with no response and no error, and any // httpResource depending on it hits NG0991. catchError(() EMPTY), ); } }HttpClient的拦截器链路HttpHandler/HttpInterceptor机制位于 packages/common/http/src/interceptor.ts凡经过HttpClient的请求都会穿过这条管道所以拦截器一旦“吃掉”错误并静默完成下游所有依赖该响应的httpResource都会落入 NG0991。修复方式与在拦截器管线中的常规做法一致二选一让错误继续传播去掉catchError让错误沿拦截器链自然抛出httpResource会据此进入错误状态换成能真正“解决请求”的东西例如of(new HttpResponse({body: fallbackValue}))用一个完整的HttpResponse作为回退响应请求便有值可交付。使用rxResource时两种常见的“空手完成”rxResource的stream完全由你编写因此更容易亲手制造出 NG0991。RxResourceOptions.stream的类型注释packages/core/rxjs-interop/src/rx_resource.ts也明确警告该 Observable 必须在完成前发出值或错误否则 Angular 会抛出 NG0991。常见原因一catchError(() EMPTY)吞掉错误最典型的写法是捕获错误后用EMPTY静默吞掉import {EMPTY, catchError} from rxjs; const userResource rxResource({ params: () ({id: userId()}), stream: ({params}) this.http.get(/users/${params.id}).pipe( // BAD: this hides the error completely. The Observable just // completes with nothing, and the resource has no value and no // error to show for it — this is what triggers NG0991. catchError(() EMPTY), ), });EMPTY不会发出任何值订阅后立即触发complete。结合上一节的源码可知此时next从未被调用hasResolved保持falsecomplete分支便抛出 NG0991——错误被彻底“隐藏”了资源既没有值可展示也没有错误可上报。常见原因二多流组合后“全部静默完成”另一种常见场景是用merge、race等操作符合并多个 Observable其中某个源可能在自己 emit 之前就单独完成且整体没有用startWith(...)保证一个初始值。如果所有组合源在发出任何值之前就全部完成了合并后的流同样会“空着”完成进而触发 NG0991。因此无论采用何种组合方式都要确保合并结果在 complete 前至少能发出一个值必要时用startWith(...)兜底。两种修复方案放行错误或提供回退值针对rxResource根据业务诉求选择下面任一方案方案一让错误穿透到资源如果不需要自行处理错误直接去掉catchError。请求失败时错误会自然流向资源使资源进入error状态你可以通过userResource.error()读取stream: ({params}) this.http.get(/users/${params.id}),这是最贴近 Angular 资源模型的做法资源本来就设计成“要么有值要么有错误”两种终态。方案二用回退值恢复如果希望界面展示兜底内容而不是错误页用catchError返回一个会发出值的 Observable例如of(...)import {of, catchError} from rxjs; stream: ({params}) this.http.get(/users/${params.id}).pipe( catchError(() of(null)), // resource resolves with null instead of erroring ),上面例子中资源最终以null作为值正常解析而不是进入错误态。注意这里不能返回EMPTY必须返回至少发出一个值的 Observable。两条方案的共同底线从stream返回的 Observable 在 complete 之前必须至少发出一个值或一个错误。错误在哪里真正抛出读取时抛错与全局 ErrorHandlerNG0991 的“抛出时机”与直觉相反这也是它容易误导排查方向的原因它不是在资源创建时抛出也不必然在请求完成处抛出而是在下一次有人读取资源的value()时才抛出——例如模板绑定中调用userResource.value()的那一刻。这意味着报错堆栈指向的位置可能离真正出错的代码很远表面看是“模板渲染报错”实际根源却在“上游”——stream空着完成或某个拦截器吞掉了响应。官方测试 packages/core/rxjs-interop/test/rx_resource_spec.ts 专门验证了生产环境中的真实失败模式组件模板里写{{res.value()}}而stream返回EMPTY当变更检测应用触发的调度 CD 周期读取该资源时错误会上报到全局ErrorHandler同时whenStable()也会以同一错误被拒绝。注释中还特别提醒如果代码在读取.value()之前从不检查资源状态这个错误就会像任何模板渲染时未被捕获的异常一样一路冒泡到 Angular 的全局ErrorHandler。降低影响的两层手段按优先级排序从根上修复使用上述方案之一让资源永远不会进入“空完成”状态——这是首选读取时加防御纵深防御针对你无法完全控制的情况例如第三方拦截器或服务意外地空完成在读取.value()之前先检查.hasValue()或.status()if (userResource.hasValue()) { {{ userResource.value() }} }而不是无条件地读取.value()。hasValue()/status()/value()/error()均为ResourceRef提供的标准读取 API相关类型定义与rxResource同源可参阅 packages/core/rxjs-interop/src/rx_resource.ts。小结排查 NG0991 的检查清单若错误来自httpResource重点检查HttpClient拦截器链看是否有拦截器catchError(() EMPTY)或类似静默完成的处理改为止于错误传播或返回完整的HttpResponse回退若错误来自rxResource审查stream函数查找catchError(() EMPTY)与merge/race等无startWith的组合确保流完成前必有值或错误定位时注意抛出点与根源点分离报错发生在读取value()处根源在流或拦截器上游作为兜底读取前用.hasValue()/.status()做防御性检查避免错误冒泡到全局ErrorHandler。相关的rxResource实战用法可继续阅读 adev/src/content/ecosystem/rxjs-interop/signals-interop.mdhttpResource的完整 API 与子函数如httpResource.text()可查看 packages/common/http/src/resource.ts。【免费下载链接】angularDeliver web apps with confidence 项目地址: https://gitcode.com/GitHub_Trending/an/angular创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价