ChatGPT解决这个技术问题 Extra ChatGPT

Angular $scope.$apply 与 $timeout 作为安全的 $apply

我试图更好地理解在 Angular 中使用 $timeout 服务作为一种“安全 $apply”方法的细微差别。基本上在一段代码可以运行以响应 Angular 事件或非 Angular 事件(如 jQuery 或某些标准 DOM 事件)的情况下。

据我了解:

在 $scope.$apply 中包装代码适用于您尚未处于摘要循环(也称为 jQuery 事件)但如果摘要正在进行中会引发错误的情况 在 $timeout() 调用中包装代码而不延迟参数是否已经在摘要循环中有效

查看 Angular 源代码,看起来 $timeout 调用了 $rootScope.$apply()。

如果摘要周期已经在进行中,为什么 $timeout() 也不会引发错误?当您确定摘要不会正在进行时,使用 $scope.$apply() 的最佳实践是,而在需要以任何方式保证安全时使用 $timeout() 是最佳实践吗? $timeout() 真的是一个可以接受的“安全应用”,还是有陷阱?

感谢您的任何见解。


C
Community

查看 Angular 源代码,看起来 $timeout 调用了 $rootScope.$apply()。如果摘要周期已经在进行中,为什么 $timeout() 也不会引发错误?

$timeout 使用未记录的 Angular 服务 $browser。具体来说,它使用 $browser.defer() 通过 window.setTimeout(fn, delay) 异步延迟执行您的函数,这将始终在 Angular 生命周期之外运行。只有在 window.setTimeout 触发您的函数后,$timeout 才会调用 $rootScope.$apply()

当您确定摘要不会正在进行时,使用 $scope.$apply() 的最佳实践是,而在需要以任何方式保证安全时使用 $timeout() 是最佳实践吗?

我会这么说。另一个用例是有时您需要访问一个 $scope 变量,您知道该变量只会在摘要后初始化。简单的示例是,如果您想在控制器构造函数中将表单的状态设置为脏(无论出于何种原因)。如果没有 $timeout,则 FormController 尚未初始化并发布到 $scope,因此将 $scope.yourform.setDirty() 包装在 $timeout 内可确保 FormController 已初始化。当然,您可以使用没有 $timeout 的指令来完成所有这些操作,只需给出另一个用例示例。

$timeout() 真的是一个可以接受的“安全应用”,还是有陷阱?

它应该始终是安全的,但在我看来,您的 go to 方法应该始终针对 $apply() 。我正在开发的当前 Angular 应用程序相当大,我们只需要依赖一次 $timeout 而不是 $apply()。


在我的项目中,我有一个场景需要提高 $scope.$digest() 以捕获发生在 Angular 视图之外的事件。超时是否仍然是 safeApply 或 safeDigest 的规范,或者这已在框架内巧妙地修复?
为什么您需要依赖 $timeout 而不是 $apply?如果您不能共享代码,您至少可以讨论一下基本原因吗?
R
Rahul Garg

如果我们在应用程序中大量使用 $apply,我们可能会得到 Error: $digest already in progress。这是因为一次可以运行一个 $digest 循环。我们可以通过 $timeout 或 $evalAsync 来解决它。

$timeout 不会产生像“$digest already in progress”这样的错误,因为 $timeout 告诉 Angular 在当前周期之后,有一个超时等待,这样可以确保摘要周期之间不会有任何冲突,从而输出 $ timeout 将在新的 $digest 周期上执行。

我试图在 Comparison of apply, timeout,digest and evalAsync 处解释它们。

也许它会帮助你。


谢谢拉胡尔,文章很有趣。我觉得它缺少的是建议使用哪个,或者什么时候使用。再次感谢。
感谢马蒂的意见。在我看来,与可用的相比,$evalAsync 是更好的选择。
J
Jeff Hubbard

据我了解,$timeoutsetTimeout 的包装器,它隐式调用 $scope.$apply,这意味着它在角度生命周期之外运行,但会启动角度生命周期本身。我能想到的唯一“陷阱”是,如果您希望您的结果可用this $digest,您需要找到另一种“安全应用”的方法(AFAIK 是只能通过 $scope.$$phase 获得)。