Post

Async/Await in a Console Application

Here’s the situation, you want to be able to build a console application and still take advantage of the asynchronous programming model available in the latest versions of VB (2012/2013).

No matter the reason behind why you may want to do this, the answer I give will still be the same; however, I’ll provide my real-world reasoning behind my desire to have Async/Await from a Console Application… TESTING. Many times it is a lot easier to do testing of API’s from a Console Application; however, if your API’s are Async/Await… thus the problem.

To do so, you may be tempted to start by modifying the Sub Main() based on the tons of examples you’ve seen for several other types of projects.

1
2
3
4
5
Async Sub Main()   
  Dim result = Await WebUtilities.FetchUrlAsync("http://addressof.com")
  Console.WriteLine(result)
  Console.ReadLine()
End Function 

This, however, will not work. Visual Studio will quickly alert you to this situation and you are left scratching your head. You then will proceed (most likely) to search the web for a possible solution. There are several out there that state that you need to setup some sort of synchronization context or some such and that you’ll need to either use one that someone else has made or build one yourself. There sure seems like there’s got to be a simpler solution…

1
2
3
4
5
6
7
8
9
Sub Main()   
  Task.Run(AddressOf MainAsync).Wait()
End Sub
       
Async Function MainAsync() As Task
  Dim result = Await WebUtilities.FetchUrlAsync("http://addressof.com")
  Console.WriteLine(result)
  Console.ReadLine()
End Function 

Hmmm… that seems to do the trick.

The Task.Run method has several overloads, one of which takes the address of an Async/Await style method. This actually kicks off the process of running that method asynchronously; however, if left to just calling this Run() method, the application would then exit immediately as nothing is keeping the Main method from completing. To work around this problem, the Wait() method is executed upon the return of the Run() method.

To keep things straight, I named the target method MainAsync(). This is where I will place all of my code that would have normally been in the original Main() method. From this point, anything I want to test; whether it be Async/Await or not, it “just works”.

A couple of side notes:

It does have to be a function, but since I’m not actually needing to return a “result”, we will be returning Task. This is necessary for the Async/Await functionality to work. The rule is either to return Task(Of [type]) or just Task. Whenever you add the Async keyword to an existing Sub in other project types, this is only allowed on events and only in projects that have an understanding of this concept. There is a ton of behind the scenes compiler magic that occurs and the only place where this should be done is where it’s been “blessed” by the platform/compiler teams. For all code that you write, stick to Function with either a result of Task or Task(Of [type]); keep it simple.

The return of the Run method occurs as soon as the first Await is encountered in the target method; so in the above example, it happens immediately. The Wait() method will pause the execution of the Main() method until the total completion of the target method.

This post is licensed under CC BY 4.0 by the author.