Improving Async Logic with Angular 2

Reducing callbacks improves the experience of working with async logic. With Angular 2 and the async pipe, we can significantly reduce the number (and/or depth) of callbacks we need to make. Let's consider a simple example.

Let's say we're writing an application that fetches articles and displays them to the user. This fictional app requires us to call an API to fetch the articles from a backend service. This usually looks something like this.

ngOnInit() {
    this.articleService.getArticles().then(articles => this.articles = articles);
}

You've likely seen this many times before. We're calling a service that returns a Promise and we're using the then callback to access the results. When our then function is called we simply set the component's articles property to the value passed to our then function. The template to render the articles looks like this:

<div *ngFor="let article of articles">
	{{ article.name }}
</div>

Overall this wasn't too bad, but we did need to use a callback to grab the articles and then assign those to the component's articles property. This is unneccessary with the async pipe.

The Async Pipe

We can improve the previous example using the async pipe. The async pipe takes an asynchronous reference, waits for the asynchronous operation to complete, and then extracts and returns the result to the template.

With the async pipe we can remove the callback from the previous code:

ngOnInit() {
	this.articles = this.articleService.getArticles();
}

That's much better. We've reduced the noise and can now clearly see the intent. However our articles property now contains a Promise not an Array so our template won't render it correctly. We need to update the template and use the async pipe, like this:

<div *ngFor="let article of articles | async">
	{{ article.name }}
</div>

Can you see the difference? We've made one change to the template, we added the async pipe to the *ngFor expression.

Advanced Example

What if we wanted to filter that async collection of articles? Let's say our requirements change, and this component now has to show read and unread articles. We might be tempted to implement it like this:

ngOnInit() {
	this.articleService.getArticles().then(articles => {
		this.readArticles = articles.filter((article) => article.read);
		this.unreadArticles = articles.filter((article) => !article.read);
	};
}

This is similar to the first example we saw. Like the first example, we don't need the async pipe but we do need a callback so that we can filter the array before setting the properties on the component. Here's an alternative syntax to achieve the same thing:

ngOnInit() {
	const articles = this.articleService.getArticles();
	this.readArticles = articles.then((articles) => articles.filter((article) => article.read));
	this.unreadArticles = articles.then((articles) => articles.filter((article) => !article.read));
}

Okay, we've actually increased the number of callbacks here, but we've reduced the depth of the code, we're left aligned as much as possible (we're not building pyramids). Personally, I prefer this syntax. It also works with RxJS which is useful for more complicated scenarios.

Like the first async example, we also need to use the async pipe in our template to render the articles:

<h1>Read</h1>
<div *ngFor="let article of readArticles | async">
	{{ article.name }}
</div>

<h1>Unread</h1>
<div *ngFor="let article of unreadArticles | async">
	{{ article.name }}
</div>

Conclusion

The point of all this is that we can improve how we handle async logic. We can reduce the number (and/or depth) of callbacks we need to write by using the async pipe. This gives a better developer experience and, in my opinion, in most cases provides cleaner code that expresses it's intent more clearly.