Angular
  • Angular learning
  • Angular
    • Change Detection
      • Angular Change Detection Strategies
      • Understanding Change Detection Strategy in Angular
    • Angular Components Overview
      • Lifecycle hooks
      • View encapsulation
    • Text interpolation
    • Pipes
    • ARIA
    • Event binding
    • Directives
    • Dependency injection in Angular
    • Difference between Template-Driven and Reactive Forms
    • Guards
    • Resolvers
      • Resolver example
  • Memory management in Angular applications
  • Renderer2
  • Angular test
    • Testing
      • The different types of tests
      • Some testing best practices
      • Angular Service Testing in Depth
        • About Jasmine
        • Jasmine - Test suites
        • Implementation of First Jasmine Specfication
        • spyOn() & jasmine.createSpyObj()
        • beforeEach()
        • Testing services
        • Disabled and Focused Tests
        • flush
        • HttpTestingController
        • Sample code
      • Angular Component Testing in Depth
        • Intro to Angular Component testing
        • DOM interaction
        • Trigger Change Detection
        • Test Card List Test Suite Conclusion
        • Window.requestAnimationFrame()
        • Asynchronous Work (Jasmine)
        • Cooperative asynchronous JavaScript: Timeouts and intervals
        • FakeAsync - Asynchronous Work (Jasmine) part 2
        • tick()
        • Sample codes
      • Testing Promised-based code-intro Microtasks
        • Microtasks
        • Micro-tasks within an event loop (Summary)
        • Macro-tasks within an event loop (Summary)
        • Test promised Microtasks (code)
      • Using fakeAsync to test Async Observables
      • Cypress.io
        • Create our first e2e test
      • Angular CLI code coverage and deployment in prod mode.
      • Travis CI
  • Angular best practices
    • Angular best practices
      • Security
      • Accessibility in Angular
      • Keeping your Angular projects up-to-date
    • Bootstrapping an Angular Application
      • Understanding the File Structure
      • Bootstrapping Providers
    • Components in Angular
      • Creating Components
      • Application Structure with Components
        • Accessing Child Components from Template
        • Using Two-Way Data Binding
        • Responding to Component Events
        • Passing Data into a Component
      • Projection
      • Structuring Applications with Components
      • Using Other Components
  • Reactive extensions
    • RxJS
      • RxJS Operators
      • of
      • Observable
      • async pipe (Angular)
      • Interval
      • fromEvent
      • Pipe
      • Map
      • Tap
      • ShareReplay
      • Concat
      • ConcatMap
      • Merge
      • MergeMap
      • ExhaustMap
      • fromEvent
      • DebounceTime
        • Type Ahead
      • Distinct Until Changed
      • SwitchMap
      • CatchError
      • Finalize
      • RetryWhen
      • DelayWhen
      • ForkJoin
      • First
      • Interview Questions
      • Zip
  • NgRx
    • What's NgRx
      • Actions
      • Reducers
      • Selectors
      • 🙅‍♂️Authentication guard with NgRX
      • @ngrx/effects
        • Side-Effect refresh survivor
  • Interview Q&A
    • Angular Unit Testing Interview Questions
    • Angular Questions And Answers
  • Angular Advanced
    • Setting up our environment
      • Understanding Accessors (TS)
      • The host & ::ng-deep Pseudo Selector
Powered by GitBook
On this page
  • Microtasks queue
  • Unhandled rejection
  • Summary

Was this helpful?

  1. Angular test
  2. Testing
  3. Testing Promised-based code-intro Microtasks

Microtasks

PreviousTesting Promised-based code-intro MicrotasksNextMicro-tasks within an event loop (Summary)

Last updated 4 years ago

Was this helpful?

Promise handlers .then/.catch/.finally are always asynchronous.

Even when a Promise is immediately resolved, the code on the lines below .then/.catch/.finally will still execute before these handlers.

Here’s a demo:

let promise = Promise.resolve();

promise.then(() => alert("promise done!"));

alert("code finished"); // this alert shows first

If you run it, you see code finished first, and then promise done!.

That’s strange, because the promise is definitely done from the beginning.

Why did the .then trigger afterwards? What’s going on?

Microtasks queue

Asynchronous tasks need proper management. For that, the ECMA standard specifies an internal queue PromiseJobs, more often referred to as the “microtask queue” (ES8 term).

As stated in the :

  • The queue is first-in-first-out: tasks enqueued first are run first.

  • Execution of a task is initiated only when nothing else is running.

Or, to say more simply, when a promise is ready, its .then/catch/finally handlers are put into the queue; they are not executed yet. When the JavaScript engine becomes free from the current code, it takes a task from the queue and executes it.

That’s why “code finished” in the example above shows first.

Promise handlers always go through this internal queue.

If there’s a chain with multiple .then/catch/finally, then every one of them is executed asynchronously. That is, it first gets queued, then executed when the current code is complete and previously queued handlers are finished.

What if the order matters for us? How can we make code finished run after promise done?

Easy, just put it into the queue with .then:

Promise.resolve()
  .then(() => alert("promise done!"))
  .then(() => alert("code finished"));

Now the order is as intended.

Unhandled rejection

Now we can see exactly how JavaScript finds out that there was an unhandled rejection.

An “unhandled rejection” occurs when a promise error is not handled at the end of the microtask queue.

Normally, if we expect an error, we add .catch to the promise chain to handle it:

let promise = Promise.reject(new Error("Promise Failed!"));
promise.catch(err => alert('caught'));

// doesn't run: error handled
window.addEventListener('unhandledrejection', event => alert(event.reason));

But if we forget to add .catch, then, after the microtask queue is empty, the engine triggers the event:

let promise = Promise.reject(new Error("Promise Failed!"));

// Promise Failed!
window.addEventListener('unhandledrejection', event => alert(event.reason));

What if we handle the error later? Like this:

let promise = Promise.reject(new Error("Promise Failed!"));
setTimeout(() => promise.catch(err => alert('caught')), 1000);

// Error: Promise Failed!
window.addEventListener('unhandledrejection', event => alert(event.reason));

Now, if we run it, we’ll see Promise Failed! first and then caught.

If we didn’t know about the microtasks queue, we could wonder: “Why did unhandledrejection handler run? We did catch and handle the error!”

But now we understand that unhandledrejection is generated when the microtask queue is complete: the engine examines promises and, if any of them is in the “rejected” state, then the event triggers.

In the example above, .catch added by setTimeout also triggers. But it does so later, after unhandledrejection has already occurred, so it doesn’t change anything.

Summary

Promise handling is always asynchronous, as all promise actions pass through the internal “promise jobs” queue, also called “microtask queue” (ES8 term).

So .then/catch/finally handlers are always called after the current code is finished.

If we need to guarantee that a piece of code is executed after .then/catch/finally, we can add it into a chained .then call.

In most Javascript engines, including browsers and Node.js, the concept of microtasks is closely tied with the “event loop” and “macrotasks”. As these have no direct relation to promises.

Remember the unhandledrejection event from the article ?

specification
Error handling with promises