First of all, I want to say this is bad practice and you should avoid it. However I am not gonna lie, I have used this trick a few times myself as well.
The previous developer added setTimeout
because A, there is probably a race condition between the behavior of various (Angular) components or B, one component changes the state of another component in a change detection cycle (indicated by the error).
The behavior the developer achieved
First, I need to explain what the developer achieves by setting his function into a setTimeout
with zero delay. Javascript is a single-threaded language, meaning by default the JS engine does one thing at a time.
The super simplified (and kind of wrong) way of explaining this is that a JS engine has a list of statements to execute in a list and it always picks up the first. When you set something in a setTimeout
you put that statement (your function call) into the end of this task list, so it will be executed when everything else "queued" up for execution until that point has been processed.
There is a great video on YouTube about this: What the heck is the event loop anyway?, I strongly encourage you to go and watch this video!
In case of a race condition
It might happen that two of your components has a race condition on each other, to demonstrate let's say the JS engine "list of tasks" looks like this:
- component A does some stuff
- component A wants to set some stuff in its child: component B
- Angular runs a CD cycle and attempt to render your component
- child component B does some stuff
The problem here is that in step 2 your child component (B) has not been created yet, so attempting to use it will throw an error. By putting the statement which modifies component B in a setTimeout
your list of tasks will look like this:
- component A does some stuff
- Angular runs a CD cycle and attempt to render your component
- child component B does some stuff
- component A wants to set some stuff in its child: component B
This way your B component will exist by the time it has been created.
In case of an inconsistent state
Angular runs a so-called change detection cycle to check what has changed in the application state and how the UI needs to be updated. In developer mode, this check runs twice for the same cycle and the outputs are compared. If there is a difference the framework will throw the mentioned ExpressionChangedAfterItHasBeenCheckedError
error to warn you about this.
At this point, you can say to yourself, great this issue won't appear in prod so I am good but you are not good this issue will lead to a worsened performance as Angular will run more change detection cycles than it should because it thinks something has changed when in reality you didn't intend to change anything. So you should track down the route cause of this problem and fix it.
The official Angular website has a dedicated document page for this with a video guide on how to solve the problem. There is also a detailed Stackoverflow answer here about this behavior and why is this checked.
So as for your original question by adding the setTimeout
the developer tricks Angular to pass this double-checking because the updateContentChildren
function will be executed only after the current change detection has finished. This means your internal state is always "one tick ahead" of the UI as some work is always finished after the CD cycle has finished.