09 Nov 2017 - by 'Maurits van der Schee'
IMHO the most important reason to not do async is that synchronous code gets executed more linearly and is thus easier to reason about. The amount of possible states in an async programming model easily explodes, which makes the code hard to read and understand. Of course there are people with strategies (like Flux) to avoid your code to turn into a big ball of mud (also known as spaghetti code), but why would you when you can better say "no" to async programming anyway?
A blocking I/O model with (a pool of) threads or processes will have lower latency per request than an event loop. A thread pool model will be equally fast as an event loop when the system is under low load. But under heavy load, the event loop will slow down. When you hit the async system with many concurrent requests, the latency will increase. The synchronous thread based model will not slow down, but start to use multiple CPU cores and in todays high-core count machines this is very well possible. You can summarize this point by simply saying that, contrary to popular belief, async is "slow".
Yes, async may have a nice memory footprint and low CPU usage for I/O intensive applications (proxying API requests for instance), but when you are building a real application, then you will need to "do" things with the data that is transferred. This will slow down the entire system as you typically will have just one single thread. If you want to do computation you probably need more threads, a thread pool and a scheduler .. well .. eventually a complete synchronous programming system. Without that it will be difficult to take advantage of the high number of CPU cores in a typical system.
We used to struggle to handle 10k connections on a server (the C10K problem). Threads used to use a lot of memory and used to cause a lot CPU usage when they started up. This is still the case if you are naively using "fork" to implement multiprogamming, but that is not best practice anymore. A good thread library will handle multiplexing green threads over OS threads and it will get you parallelism on top of a synchronous programming model. In the Haskell programming language you find a very good and light weight threading implementation. Similarly the Go language has a CSP based routines and channels implementation that makes it easy to handle a high connection count in a synchronous way. To illustrate the differences: an Apache thread can easily take up 10 MB of memory, while a JVM thread may only take up 1 MB. In the Go language we have "go routines" which have only a 8 KB stack size.
Expensive calls will slow down the entire system and solving that problem is harder and less understood than solving deadlock (or livelock) issues. Deadlocks can be avoided by relaxed consistency (non-exclusive usage) and exclusive use of your resources in a fixed (f.i. alphabetical) order. When you have users using more CPU cycles than is optimal for your event scheduling system then you have no such clear way out of misery.
When you have a performance issue in a synchronous threaded application you can simply inspect the call-stacks via sampling or dumps. This is far less easy for an async system. There you need to follow the messages between the different handlers (potentially on different threads). This would be doable with proper tooling, but there is not so much tooling available for async and there is a less good understanding of common strategies for performance analysis and improvement.
When you are programming a chat room or a game, then an async model where all connections are handled by a single thread makes your implementation easier without a doubt. But most applications are not chat rooms or games. They are administrative applications. In these applications you typically need to communicate with an underlying database to be able to display the next form/step/result/overview. There is no point in allowing other interactions (than the main one) to take place when working in such a system. A better multitasking experience (than async/reactive programming) is probably achieved by using the tabs of your browser: one for every activity.
Everything communicates with HTTP and HTTP is synchronous. You request a page and receive it. You send a form and get a page back. There is noting asynchronous about it. There are initiatives such as secure web sockets (WSS) and server side events (SSE), but with the presence of HTTP/1.1 (keep-alive) and the arrival of HTTP2 (multiplexing) I expect that the usage of these protocols will diminish.
You should probably try to master synchronous programming instead of investigating hypes like async. Most of the product software won't require it anyway. Synchronous programming is by no means easy, but it is without doubt mainstream and thus valuable. Also, there is a lot of innovation going on in synchronous programming, like the ultra light weight multiprogramming in Go. My advice: Invest your time wisely!