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).
See the screen shot below of some asynchronous code from an application I am currently working on.
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.
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:
The entire run method looks like this (just to give some context)
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:
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!
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!