Using Typescrip 2.1 async and await in an Angular 1 application (Yes! Yes! Yes!!)

03 februari 2017  |  
4 min leestijd

Two blogposts in one week? Yes! I was so excited about this subject I could not stop myself from sharing this with the rest of the world. As a C# developer, I actually love the async and await feature that that language offers. I think it makes asynchronous code a lot easier to write and to read (although you do need know what is happening under the hood).

Asynchronous code is not unique to C#. It also happens a lot when you are writing JavaScript /TypeScript. When you are using Angular 1, you will probably know that Angular uses promises to represent a piece of work that finishes in the future. Angular promises and the Angular $q service that you can use to work with promises are based on the CommonJS specification for promises. These promises are working fine, but they can cause your code to become less readable because of all the callbacks that are happening when doing promise programming, especially when you have nested asynchronous calls together with failure handlers for the promises.

See the screen shot below of some asynchronous code from an application I am currently working on.

Capture

On itself this is not one of the worst parts, but you can see some nesting going on and some then-calls with anonymous functions. I would love to rewrite this to the new async/await features of Typescript 2.1.  Also take note of the way I create a promise that resolves immediately, using $q.when().

Async/await was available before Typescript 2.1 but it was only available when targeting ECMA 6 or ECMA 2015. Typescript 2.1 adds support for down level async functions, meaning that we can also target ES 5 or 3.

When using async/await on a ES 5 or 3 environment we have to make sure we have an ES 6 compatible  promise implementation. The generated JavaScript, when using async/wait is based on the ES 6 Promise specification. So we need to polyfill the Window.Promise constructor function. But we can’t just use any polyfill because in an Angular application, the default $q promises also call $apply when a promise resolves to refresh the UI. If we would grab just a polyfill from the internet we have to add the $apply calls ourselves. That would be a lot of work.

The trick to solve this is really easy! Luckily angular comes with an ES 6 polyfill out of the box! If you look at the documentation for $q, you will see that it also has an ES 6 compatible interface. Instead of using the defer api when creating promises, you can also use the ES 6 way of creating promises. You can actually use the new keyword on $q and pass it a function to create a new promise. Or instead of using $q.when to create a promise that resolves immediately you can also use $q.resolve, just like in the ES 6 specification.

So now that we have established that we can use $q to polyfill the window.Promise, here is the way to do it.

In the run method of my  main module I injected the following piece of code:

Capture

The entire run method looks like this (just to give some context)

Capture

The promise polyfilling happens on the last line of the run method.

So that takes care of the runtime stuff. Runtime, the generated async code has everything it needs to run without errors. But compile time we are still not there. Typescript does  not know that we have a valid Promise implementation when targeting an ECMA version lower than 6. The Typescript compiler has a new option for this. The –lib option. You can pass this option an array of strings, to tell the compiler which standard declaration files the compiler should be using when compiling. I am compiling my application by using gulp and gulp-typescript, so my gulp file has been modified to look like this:

Capture

You can see the list of strings I pass to the lib option. The ES2015.Promise string does the trick for the promises. Normally this list is populated for you when you specify a target. But when you pass the lib option yourself, this does not happen so I also have to include the other declaration files that I want to be there (for accessing the DOM for example).

Now we have everything in place to rewrite the code that we saw earlier is to async/await. Here it is!

Capture

This looks a bit more readable! There are no more anonymous functions for hooking then handlers to promises. I can also use the normal try catch constructs if I want instead of the failure handlers. Also notice The Promise.resolve() call I now use to create a promise that completes. This more ES 6 style.  I also modified the services to return Promise instead of ng.IPromise to make sure the compiler does not complain, runtime the types are interchangeable.

One last tip. If you are using gulp-typescript to compile, make sure you use version 3! As version 2.x grabs it’s own version of typescript (you can override this), but version 3 will grab the typescript version you have in your package.json.

Happy asynchronous coding!

Chris

03 februari 2017  |  
4 min leestijd

Kennismaken?

Luminis onderscheidt zich van andere aanbieders in de ICT door een waarde- en klantgedreven aanpak. Wij willen vooruitgang zien, zowel bij onze klanten als bij onze medewerkers. Graag komen we met je in contact om te ontdekken hoe we die kunnen realiseren.

Contact

©2019 Luminis Arnhem – Leveringsvoorwaarden

Design: Eyefun