What was that ‘flushMicrotasks’ thing again?

Georgi Parlakov
Jan 6, 2019 · 4 min read

(Testing Promises in Angular.)

The one used in Angular testing — yes that one. What is is flushing exactly? And more importantly — when do we use it?

We use flushMicrotasks inside of a test zone to control the execution of all tasks in the microtask queue.

Consider the following Stackblitz:
(I got some feedback that on mobile browsers Stackblitz is hard to use — so see gist below if that’s the case for you)

There is an AuthService that has the getUser method. It returns a Promise<User>. The hello.component should redirect to /login via Router when user is the empty user. Simple enough, right. Turns out that test fails.

If you open the Stackblitz and at the hello.component.spec.ts and comment out or delete the pending call on line 27, and run it the test breaks. It breaks even though we’ve specifically mocked out the AuthService to return the resolved promise of the empty user. It follows the logic and the HelloComponent should redirect. And it does. On line 34 — we log out to console that we actually call the navigate method on the mocked router. Wait what? Is jasmine broken? What happens — we want to have been called and it has — so why jasmine u no see?

The answer is java script and the event loop. See explaining what a microtask is and how it differs from a task (aka macrotask in angular/zone.js) Know it, learn it, love it as it goes.

In short tasks must wait for the browser to finish a loop (as in event loop) before they get the chance to run some JS, while microtasks get to run JS immediately after the call stacks is empty and if more microtasks are scheduled from one microtask — they get executed immediately after. Things like Promise-s and MutationObserver-s. Here’s a glimpse of the implementation from core.js which gives a good idea of how Promise is implemented. It steps on the , which in turn uses the .

Here’s a rundown of what happens, step by step in our failing test. Keep in mind java script is single threaded so everything happens synchronously.

Maybe a bit too small but the important part is the stack and the queue.
  1. Jasmine calls our test and it starts i.e. pushes its frame on the call stack.
  2. We define our mocks i.e. mutate the jasmine mock objects instructing them how to behave (they push and pop their frames on the call stack also — but that’s not interesting for our case)
  3. We call component.login() so now it gets on the call stack.
  4. Now inside login we synchronously call authService.getUser (which is why the first test passes). It pushes its frame.
  5. The mock kicks in and constructs a Promise and immediately returns it. The Promise is scheduled as a microtask. The mock returns. It’s frame is popped out of the call stack and we are back in the login frame.
  6. login frame has nothing else to do so it returns and get’s popped off the stack
  7. Now we are in the it frame where the expect follows and gets executed and fails the test. At this point the promise has not had a chance to execute its callback because it is waiting for the call stack to be empty.
  8. After call stack drains, promise is pushed on the call stack.
  9. It does its thing and logs out ‘navigating’ to the console.

Now it’s clear why the test failed — expect is executed before the router.navigate. But what can we do to make sure all the microtasks are executed before the test gets to finish? setTimeout? That might work (it will) but can we do better?

How about giving instruction to the microtask queue to flush? Enter flushMicrotasks. We can use the fakeAsync to execute the test inside a zone and we can have control over the execution of microtasks (and macrotasks). That’s what we do in the next Stackblitz (or gist below):

The difference here is right after step 6. At that time we call flushMicrotasks (line 30 or line 7 in gist). And that takes the resolve(User.Empty) and pushes it onto the call stack which triggers the router.navigate('/login') and at the time the expect gets called we have called navigate and it passes the test!


There you go, we use flushMicrotasks inside of a test zone to control the execution of all tasks in the microtask queue.


For the longest time I really did not understand the fakeAsync and what it’s for. I hope I’ve managed to share some of the revelation of flushMicrotasks. It happened to me when I was having a very similar failing test to what is seen in the initial example. And bumping my head for a non-trivial amount of time. The thing that helped me was the knowledge that promise uses microtasks. So thanks to Jake Archibald for his great I strongly encourage you to look into the event loop and how it works. It’s proven to be a core piece of knowledge again and again.

Another important Angular piece of the puzzle is zonejs You may want to look into the Max Koretskyi, aka Wizard . (thumbs up, Max and all the rest of the authors, for the blog).

Thanks for reading. :) (and sorry it’s been so long since the last ng-gotcha but I’ve been )

ng-gotchas

I had more than one "Oh angular gotcha!" moments and I want to share them.

Georgi Parlakov

Written by

Angular and DotNet dev. RxJs explorer. Testing proponent. Interested in all things dev, especially when result is code not scary to change(or touch:). A dad.

ng-gotchas

I had more than one "Oh angular gotcha!" moments and I want to share them.

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade