setTimeout considered harmful
Recently I learned the hard way about setTimeouts side effects.
101 setTimeout issue
Let's say we have a following piece of code:
And unit test:
Of course test passes, and we are happy.
Then, after some time, because of a reason (e.g., we want to fix some bug), we are adding setTimeout to changeVar function:
Unit test does not pass anymore. So we are adding setTimeout to our unit test as well (ideally: appropriately longer than one in changeVar function to avoid confusion!):
Then, we are introducing change to our code. Update only when someVar is not zero. We update function, and test accordingly:
Everything works. Great! Then, somebody else is fixing another bug - of course by increasing timeout:
Still works, but when after some time we decide to change our logic:
Our test is still passing...when it shouldn't!
Of course all of this is happening in large codebase with more complex logic.
But this is not that bad. Let's take a look at more interesting scenario.
More complex case
We are in worst situation when we have waterfall of setTimeouts.
Guess if this unit test will pass:
You think it should? You are wrong!
But this test will pass:
Do you see the difference? Yes, timeout is 21 instead of 20. And of course everything will fall apart if somebody increase timeout in fun1 or fun2.
The best solution is not to use setTimeout at all. Unfortunately we need async operations sometimes. In this case - you should use promises if possible. Unfortunately World is not perfect, especially Web Development World, and sometimes you have to use setTimeout (e.g., for UI effects etc.). You may think that if you set long enough timeout in your unit tests, everything should be good, right? Well...remember that it will make your unit tests slower. Instead - you should use polling approach.
To apply it to the last example - using "Without Deferreds" approach - copy poll function to your codebase:
And call it in your unit test:
Now, you do not have to guess what value will be appropriate for setTimeout delay, and you will speed up your tests as well.
When I see some strange behavior in code, the first thing I am looking at are setTimeout calls.