In my ember-element-query addon I have this code:
const _eqResizeHandler = () => {
scheduleOnce('afterRender', this, this.eqUpdateSizes);
next(this, this.eqUpdateSizes);
};
this.setProperties({_eqResizeHandler});
window.addEventListener('resize', _eqResizeHandler, true);
It used to work flawlessly, but now resizing the browser tab causes the app to carsh with "infinite rendering invalidation detected".
The problem happens when I'm the window for several seconds. The amount of time and movement required for the app to crash is arbitrary. Sometimes it crashes almost instantly, sometimes it takes like 10 seconds to crash.
I tried replacing next() with throttle() and increasing the timeout to unreasonably large values -- and it was still crashing, so I'm not even sure that the crash is caused by this code.
These lines in ember-glimmer seem to be the cause:
let loops = 0;
function loopEnd(current, next) {
for (let i = 0; i < renderers.length; i++) {
if (!renderers[i]._isValid()) {
if (loops > 10) {
loops = 0;
// TODO: do something better
renderers[i].destroy();
throw new Error('infinite rendering invalidation detected');
}
loops++;
return backburner.join(null, K);
}
}
loops = 0;
}
backburner.on('end', loopEnd);
I see three problems with this code:
I'm sure there's perfect reasoning why such a small magic number was chosen, but:
I'm not sure what's going on here. I've added a console.log(loops) into that for cycle. I've been jerking the window for like a minute, and the app didn't crash, and loops never reached more than 2, 99.99% time being within 0-1.
And then on another try it reaches 11 almost instantly and dies.
When nested element query components are added, the app won't even start.
I emphasize that it used to work flawlessly before this check was introduced.
why you have
next(this, this.eqUpdateSizes); right after scheduleOnce('afterRender', this, this.eqUpdateSizes);
 @lolmaus I believe removing next(this, this.eqUpdateSizes); will solve the problem
@lolmaus if you really need next(this, this.eqUpdateSizes); try separating it into separate method and call it will debounce or throttle, run.throttle(this, this.nextEqUpdateSizes, 200), or run.debounce(this, this.nextEqUpdateSizes, 200)
This is looping the loop, I agree it should be configurable but it isn't too low, this happens when you invalidate rendering after render (call Ember.set) any did hook didinsert update or render. It is very rare you should need to do this. Redoing the render loop 10 times is quite a lot and you can lock up the browser doing this
I'm building an element query addon.
When a component detects its size has changed, it must update HTML classes on itself, so that its CSS can recalculate. It should happen in afterRender. If I do it in next, there's a flash of unstyled content (component size changes but its content is styled for previous style).
And then every element query component must recalculate if its parent element query component updated.
it is ok to measure and do another pass, > 10 seems excessive
Easy to hit that button on the phone
it is ok to measure and do another pass
As I said, this causes a flash of unstyled content.
@lolmaus I notice the // TODO: do something better comment in the glimmer code you mentioned. so I think there is some awareness about future refactoring needed.
I don't think this is a bug for the Ember.js repo though. Perhaps a feature request for Glimmer? That said I think there is a reason the number 10 was selected. Is there a way you can throttle so that your addon can work within that limitation?
@lolmaus what do you think about opening the issue in the Glimmer repo?
@pixelhandler The offending line is in the Ember repo. I'm not even sure pure Glimmer apps are affected.
OK, so I've completely rewritten my ember-element-query addon to ensure all updates are done efficiently.
With my current implementation, element query-driven components register in a tree-like structure. Only root EQ components react to a window resize event, then they tell their children to resize, and the resize event propagates recursively from parents to children.
This ensures no component has to update more than once, i. e. once for a window resize and then once after every of its EQ parents realigns.
But the issue still happens. When EQ components are nested deep enough, the infinite rendering invalidation detected error crashes the app on continuous resize.
I was able to work around the issue by wrapping event propagation from a parent to children into run.next, but this makes event propagation noticeable to naked eye: instead of all components realigning at once, you can see how they realign sequentially. This makes native Ember implementation of element queries worse than any generic implementation.
Commenting out the error throwing line (and renderer destroying line) makes it work correctly. It seems to have no practical purpose other than putting a spoke in the wheel. :angry:
Please fix it.
Not sure how helpful this is: I had a similar issue today while iterating over a model fetched through findAll. I was using the component once on the header of the page and whenever iterating over the records, I had one instance of the component for each model object. Whenever I commented either the component of the header or the multiple iterations of it the issue would go away.
After two hours, changing the way I fetched the model from findAll('model-name') to query('model-name', {}) fixed it. I just don't know enough about Ember to say if this is something relevant or not, but wanted to report : )
@dballona thanks for the comment on using query.
@lolmaus I'm curious if you still have this issue?
Haven't checked recently, but since the artificial limit still exists, I assume yes.
My use case is to do arbitrarily deep rendering invalidation on purpose.
I believe this necessity is the reason why element queries haven't become a web standard. It's slow, yes. But I know what I'm doing! â„¢
I just wish Ember didn't put a spoke in my wheel.
Thx to @locks for bringing attention back to this issue.
The up-to-date location for the relevant code is here.
Instead of if (loops > 10) { it now has if (loops > ENV._RERENDER_LOOP_LIMIT) {.
So, technically, this issue has already been solved by @rwjblue and @krisselden in ff8338dc (BUGFIX lts) Enable maximum rerendering limit to be customized.
Before this issue can be closed, though, I humbly request for this feature to be documented, so that addon authors could either manipulate _RERENDER_LOOP_LIMIT directly or have the "Installation" section of their readmes describe how addon consumers should adjust _RERENDER_LOOP_LIMIT by hand.
PS Thanks to @krisselden, @rwjblue and everyone involved for recognizing the described issue as a bug. I had to abandon my addon because of the issue and had been quite discouraged with the fact it had not been recognized as a problem, rather as a feature. Now, when the fix is documented, I'll be able to get the addon back on track.
We are very unlikely to document this more than is done in ff8338. At this point, this is still a private API and if you check the box for private APIs you do see the documentation:

Sorry this dragged out so long and for missing to close this when we made things configurable.
Most helpful comment
OK, so I've completely rewritten my ember-element-query addon to ensure all updates are done efficiently.
With my current implementation, element query-driven components register in a tree-like structure. Only root EQ components react to a window resize event, then they tell their children to resize, and the resize event propagates recursively from parents to children.
This ensures no component has to update more than once, i. e. once for a window resize and then once after every of its EQ parents realigns.
But the issue still happens. When EQ components are nested deep enough, the
infinite rendering invalidation detectederror crashes the app on continuous resize.I was able to work around the issue by wrapping event propagation from a parent to children into
run.next, but this makes event propagation noticeable to naked eye: instead of all components realigning at once, you can see how they realign sequentially. This makes native Ember implementation of element queries worse than any generic implementation.Commenting out the error throwing line (and renderer destroying line) makes it work correctly. It seems to have no practical purpose other than putting a spoke in the wheel. :angry:
Please fix it.