资讯专栏INFORMATION COLUMN

Angular $rootScope:inprog 问题探究

shiguibiao / 427人阅读

摘要:场景和问题这几天在写一个。因为状态的改变由另一个插件控制,不在的中。前者强制触发一次,后者让一段代码执行完成后触发。所以直接在回调中使用就可以解决问题。参考资料对异常的描述。这种异常附带在线文档的方式还是很方便的。

TL;DR

这是一个关于 $rootScope:inprog 错误在什么样的情况下被触发,和如何解决的故事。

场景和问题

这几天在写一个 service 。这个 service 中有个状态需要注入到 directive 中做页面展现。因为状态的改变由另一个插件控制,不在 Angular 的 event loop 中。为了触发 dirty-checking 我在 service 中调用了 $rootScope.$digest()

service 代码大概如下所示:

const STATUS = {
  A: "A",
  B: "B",
}

class SomeService {
  constructor($rootScope) {
    this.$rootScope = $rootScope
  }

  start() {
    this.plugin = initPlugin

    // Register plugin callbacks
    this.plugin.onStateA = () => { this._setStatus(STATUS.A) }
    this.plugin.onStateB = () => { this._setStatus(STATUS.B) }
  }

  _setStatus(status) {
    this.status = status
    this.$rootScope.$digest()
  }
}

angular.module("app.someMod").service("someService", SomeService)

目前为止一切正常,直到因为需求改动,需要加一个状态,这个状态的改变是通过 directive 中的按钮触发的,于是我在 service 中加了一个方法,在 directive 中调用,代码如下:

// In service
class someService {
  connect() {
    this._setStatus(STATUS.C)
  }
}

// In directive, the "btnClick" is bound to an element"s ng-click
scope.btnClick = () => {
  someService.connect()
}

然后一点击按钮,程序就跪了…… 控制台中报的错误是 $rootScope:inprog 。

解决方法

这段错误的官方描述如下:

At any point in time there can be only one $digest or $apply operation in progress. This is to prevent very hard to detect bugs from entering your application. The stack trace of this error allows you to trace the origin of the currently executing $apply or $digest call, which caused the error.

简单来说,$digest$apply 是用来触发 dirty-checking 的方法。前者强制触发一次 dirty-checking ,后者让一段代码执行完成后触发 dirty-checking 。但是 Angular 一次只允许一个 $digest 或者 $apply 运行。上面例子里的代码会挂,是因为 scope.btnClick 本身已经在 $apply 中执行了,但 someService.connect 内部通过 _setStatus 又调用了一次 $digest ,这就触发了两次。

这让我反思为什么要手动调用 $digest ?其实我的目的只是确保所有状态改变都触发 dirty-checking 。因为这个 service 中哪些代码不会触发 dirty-checking 是很明确的,那就是插件回调。所以直接在回调中使用 $apply 就可以解决问题。

修改后的代码如下:

// In service
start() {
  // Wrap code in $apply
  this.plugin.onStateA = () => { this._wrapStatusChange(STATUS.A) }
  this.plugin.onStateB = () => { this._wrapStatusChange(STATUS.B) }
}

connect() {
  // Change status directly
  this.status = STATUS.C
}

_wrapStatusChange(status) {
  this.$rootScope.$apply(() => {
    this.status = status
  })
}
总结

只在必要的时候使用 $apply 处理那些不会触发 dirty-checking 的代码。大部分的时候 $digest 都可以被 $apply 取代。

参考资料

$rootScope:inprog
Angular 对异常的描述。这种异常附带在线文档的方式还是很方便的。顺带一提 React 的异常信息也是这样。

$rootScope.Scope
Scope 的 API ,里面可以查到 $digest 和 $apply 的详细解释。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/79065.html

相关文章

  • 使用AngularJS构建应用时遇到的问题及解决方案(版本为1.3.9)

    摘要:最近在公司使用用完成了一个项目,在此记录一下过程中遇到的问题及解决方案。其他两种方法可参考站内文章控制器如何通信结语以上为我在编写一个应用时遇到的问题及解决方案,记录并分享出来,欢迎大家指正 最近在公司使用用AngularJS(1.3.9)完成了一个项目,在此记录一下过程中遇到的问题及解决方案。 使用$http服务发送ajax请求时后端无法判断请求是XMLHttpRequest 问题...

    cuieney 评论0 收藏0
  • GitHub 2017年大数据报告

    摘要:年,社区已经拥有万开发者工作在万个代码库中。这张地图近似的显示哪个大陆使用最多。自年月共有万个公共和私有的拉取请求被合并。今年的拉取请求比去年增加了。多年来,我们已经举办了数百个活动,并与世界各地的非营利组织建立了伙伴关系。 本文原创首发于公众号:ReactNative开发圈,转载需注明出处。 数百万的开发者使用GitHub来分享代码和打造企业。你来此的目的不仅是完成你的工作,获取新...

    luoyibu 评论0 收藏0
  • [译] Angular 的 @Host 装饰器和元素注入器

    摘要:装饰器我们为啥要讨论元素注入器而不是装饰器这是因为会把元素注入器依赖解析过程限制在当前组件视图内。但是一旦使用了装饰器,整个依赖解析过程就会在第一阶段完成后停止解析,也就是说,元素注入器只在组件视图内解析依赖,然后就停止解析工作。 原文链接:A curious case of the @Host decorator and Element Injectors in Angular 我...

    marek 评论0 收藏0
  • [在线+源码]vue全家桶+Typescript开发一款习惯养成APP

    摘要:基于的版本和编写的模仿原生应用的源码地址欢迎项目演示地址建议直接添加到主屏幕端体验差一些前言为什么做这个项目学习全家桶,很长一段时间在用。作者声称之后增强了对的支持,探究在中的支持情况。 vue-ts-daily 基于Vue.js的2.5.13版本和TypeScript编写的模仿原生应用的WebApp.源码地址 欢迎star 项目演示地址 showImg(https://segment...

    fantix 评论0 收藏0
  • OneAPM大讲堂 | 谁更快?JavaScript 框架性能评测

    摘要:表格底部的减速几何平均是一项总的性能指标,从左至右,依次表明了各个框架的评级。最左端是,表示无框架的实现,做为一个参考点。和都是显著偏慢的框架,两者给出的性能数据也相差无几。 文章系国内领先的 ITOM 管理平台供应商 OneAPM 编译呈现。 网页性能是一个丰富且又复杂的话题。在本帖中,我们会将讨论的范围局限在前端 JavaScript 框架上,探究相对于另外一种框架而言,使用当前的...

    BDEEFE 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<