Comparing asynchronous patterns between C# and JavaScript

In my job I spend a lot of time writing both C# and JavaScript. The code is often related to web applications or services thus usually contain a lot of asynchronous behavior involving http requests from client to server or from server to other dependent service. Often times when picking up new feature that requires switching languages my mind is still thinking in the patterns of the other language. Sometimes this is good because it offers simple reference solution to the problem and other times I think it can block me from fully utilizing the features of the language and writing the code most natural way for that language.

For example, when switching from JavaScript to C# which has more powerful control over async code with Tasks sometimes I’m still stuck thinking in Promises. Tasks have extra features like having the ability to be created without starting execution, and the ability to be canceled, and more find grained creation options, but yet they don’t automatically unwrap like Promises which is actually the most common pain point so it’s not always true that more features means you have more tools to use to get the job done in the best way possible. Sometimes simpler primitives can work better.

Anyways, enough background, I think most people already know these patterns as there have been blog posts from experts such as Stephen Cleary but I wanted to force myself to write these down to better remember the differences and keep list for future reference.

Wrapping non-async code

C# has TaskCompletionSource and JavaScript had Deferreds. Deferreds have gone out of style in favor of new Promise constructor but to compare that pattern we can use Task.Run.

C#: TaskCompletionSource
JavaScript: Deferred
C#: Task.Run
JavaScript: Promise

Modifying return value of async call

C# as task.ContinueWith and JavaScript has Promise.then and both of them also have async/await.

C#: Task.ContinueWith
JavaScript: Promise.then
C#: async/await
JavaScript: async/await

Chaining async operations

Call async operation A then call async operation B. The example we use below involves taking two related async operations and combing there result to construct larger domain object.

C#: Task.ContinueWith
JavaScript: Promise.then

There is one major difference between ContinueWith and then. Promises automatically unwrap! This greatly reduces the complexity the developer has to consider.

C#: async/await
JavaScript: async/await

Aggregate many async tasks

Technically chaining async tasks above to form larger object was a type of aggregation but those tasks were done sequentially. This pattern demonstrates running the tasks in parallel and waiting for them all to complete. In the example below imagine you have to fetch a bunch of individual items from the database and add up the value of a property.

C#: Task.WhenAll
JavaScript: Promise.all

Branching Async Tasks/Building Tree

Now for something more complex. This really is just a more advanced application of different techniques above but is more of real-world example than one-off.

In this scenario each task returns an array of items, which is used to generate another array of tasks, and so on until reaching the leaves of tree and then whole object is returned. For the use case imagine two models which are related to each other and the client wants to construct this relationship; however, the service API only returns IDs of the related models. The client must get the parent model, then request the related models and construct the full object.

Sample interfaces

RemoteNode simulates the response from server which only includes the id’s of the related items; however, the client constructs the full node which includes full references to other nodes.

Again we will compare Task.ContinueWith/Promise.then with Async/Await

Now using async/await

Overall, the differences are minor and it’s good to see the parallels between the languages. They are not so different after all. If you become strong at dealing with async behavior in one language it should be transferable to the other. The most notable thing is that Promises automatically unwrap as mentioned above and in my opinion this often makes dealing with these basic async operations feel easier in JavaScript even though it has less language features and configuration to help you solve the problem.

That being said C# has the ability to create Tasks which are not started, and also the ability to cancel tasks. Both of these should come to JavaScript native through generators and cancellation spec but until then there are some types of operations that will remain overly difficult because the primitives are not there yet.

All of the code is from the gists is in working projects you can view or download here:
https://github.com/mattmazzola/asyncpatterns