问题 何时以及为何在Angular 2中使用'tick'?


我已经看到在Angular 2组件类中使用了以下内容:

setTimeout(()=>{}, 0);

这意味着,在0秒后调用一个空函数。我知道这与Javascript事件模型有关,但完全不了解它。

请解释何时以及为什么在Angular 2中使用一个小的 现实世界的例子 和一些 代码段


1983
2018-01-15 09:50


起源

你能提供一些你见过的地方吗? - Ced
我想他想知道你为什么要设置零间隔的空回调。箭头功能部分是偶然的。 - Tim Consolazio
很难说在这个代码的使用环境中看到了什么。 - Günter Zöchbauer


答案:


setTimeout(()=>{}, 0);

导致Angular为整个应用程序运行更改检测,例如 ApplicationRef.tick

zone.js补丁异步API(addEventHandlersetTimeout,...)并在回调完成后运行更改检测。


7
2018-01-15 17:16



嗯。 “tick”是Angular框架的一部分,所以你希望它以某种方式运行。但是setTimeout只是普通的'js',除了在作业队列上放置回调之外别无其他。您似乎建议仅在有要处理的作业队列时才能保证更改检测运行,因此为了确保触发,您只需在浏览器作业队列上转储空回调?如果是真的,那听起来像A)一个巨大的框架问题和B)这个问题的hacky解决方法。我对此持怀疑态度(因为任何人都会看到一个带有空CB的0区间)。 - Tim Consolazio
@TimConsolazio检查我的答案,不是100%肯定 - Ced
你能提供一个链接吗? tick 你指的是。不,这不是关于队列,而是关于zone.js修补异步API并在回调完成后运行更改检测。 - Günter Zöchbauer
@TimConsolazio不确定你对“两个”的意思。 ApplicationRef.tick() 和 setTimeout(()=>{},0) 是等价的(如果 setTimeout(...) 在Angular中运行,因此在区域内运行) - Günter Zöchbauer
您也可以在区域外执行(按区域api)。但同样,你必须知道这一点。我参加过由Angular团队主持的一些Angular 2聚会,并没有人提到这一点,尽管他们确实讨论了区域是如何成为新的摘要。 - Tim Consolazio


我会补充Gunter的答案,因为它现在很小。

他说 :

setTimeout(()=>{}, 0); 导致Angular对整个应用程序运行更改检测

这是因为 setTimeout 方法已被猴子修补,以通过角度拦截,在拦截时触发改变检测。换句话说,每次 setTimeout 被称为变化检测发生。

这可以这样做:

window.setTimeout = () => {
    //do the usual setTimeout work
    angular.triggerChangeDetection(); // or w.e they use
}

所以0是让变化检测立即发生而空函数是因为我们不想做任何事情。


1
2018-01-15 17:29



为了完整,似乎这是准确的。 setTimeout的行为已被无形改变。对于考虑到这一点的代码(因此知道何时在区域外运行代码),这可能没问题。但是进口的库等等,对那些有什么影响?无论其上下文如何,每一次间隔踢,都会发生变化检测?这是一个性能问题,它可能在旧的库中有不可预测的行为吗?你可以通过拨打一些太空的超时来为它的膝盖带来棱角分明吗? - Tim Consolazio
@TimConsolazio我实际上并没有想到这一点。也许这是一个你是对的瑕疵 - Ced


我记得很久以前就问过这个问题了。响应,在一个名为angular-flexslider的github repo中,它是一种组件级别的替代品 $(document).ready(function() {...}))

页面加载后,Angular会操纵DOM。当CPU有点繁忙时,有时可以在页面加载后看到角度表达式更新。超时功能确保在角度处理完文档后零代码运行代码。

我从来没有这样做过,现在有了组件生命周期钩子我无法想象你再次需要(也就是说,如果你真的需要)。但从角度来看,这就是我见过的原因。我还可以说我已经看到很多由工程师编写的Angular代码,你对它们有很高的信心(就他们对Angular的知识而言),从来没有见过任何人这样做过。

此外,它还可以确保您要运行的代码位于当前事件循环队列的末尾。它通常用于表现。我曾经为“无限滚动”列表执行类似下面的操作(现在它是所有RxJS,Observables等)。考虑以下代码:

var res = []
function response ( d ) {
  var chunk = data.splice ( 0, 1000 );

  res = res.concat ( chunk.map ( ...do something ) );

  if ( data.length > 0 ) {
    setTimeout ( () => { response ( data ) }, 0 );
  }
}

因为你是递归调用的 response 通过 setTimeout 要处理块,即使它有0个间隔,你也是批量执行,并且每个批处理都将在当前作业队列的末尾处理(而不是仅仅在事件队列中构建一堆事件,这将是阻止)。


1
2018-01-15 09:57



OP功能是空的,所以它没什么吸引力。当时不存在生命周期钩子吗?难道只是在没有做任何事情的情况下只做一次蜱传? - Ced
我已经修改了我的答案,在非正式角度方面更完整。 - Tim Consolazio
我最终不知道为什么有人会在零间隔设置空回调。效果只是在作业队列的末尾放置一个空回调。我甚至不确定JS运行时在优化方面会做些什么(它或多或少会忽略它?)。我还可以说我已经看过很多Angular代码,很多代码都是由你希望了解它们的框架代码编写的。我没见过任何人做过0次间隔超时的空回调。 - Tim Consolazio
似乎枪手发现了原因:p - Ced
在项目中只需要这个,我必须计算 scrollTop 在DOM更新后立即进行。 ngAfterViewInit给出了错误的值,修复是0ms setTimeout。 - DarkNeuron