0

VB.NET does not support Async Main. This code precompiles but fails a build:

Friend Module SomeConsoleApp
  Friend Async Sub Main
    Await Task.Delay(1000)
  End Sub
End Module

The build error, of course, is:

The 'Main' method cannot be marked 'Async'

I've seen constructs such as this, which at first glance would seem to greatly simplify the situation:

Friend Module SomeConsoleApp
  Friend Sub Main()
    Dim oTask As Task = SomeFunctionAsync()
    oTask.Wait()
  End Sub

  Friend Async Function SomeFunctionAsync() As Task
    Await Task.Delay(1000)
  End Function
End Module

But I believe that to be too simple. It runs, yes, but it'll deadlock if SomeFunctionAsync performs a blocking operation that waits for completion.

What's the safest (and easiest) way to call an Async function in a VB.NET console app?

InteXX
  • 6,135
  • 6
  • 43
  • 80
  • I'm not completely certain it will deadlock (the contexts where I've seen warnings against using the blocking operations on `Task` are either UI or web host context), but I think you would be able to write an `Async Sub` which could be called from `Sub Main` which would `Await` whatever async operations. The risk is that `Async Sub` is more likely to result in a crash if there is an error. – Craig Mar 27 '23 at 14:05
  • 1
    `Async Sub` That leaves me with a sticky feeling. I know, technically it should be OK, since we'll be sure to catch all exceptions within it, but what if we skip that important step by accident? Cleary's solution (see my answer) seems pretty solid. The limitations there, of course, are framework support levels. He doesn't appear to be doing much maintenance lately. – InteXX Mar 27 '23 at 14:13

1 Answers1

1

This can be accomplished safely with Stephen Cleary's Nito.AsyncEx utility:

Assuming your project is compatible with its version support, install the package and use a construct similar to this:

Friend Module SomeConsoleApp
  Friend Sub Main()
    AsyncContext.Run(Function() SomeFunctionAsync())
  End Sub

  Friend Async Function SomeFunctionAsync() As Task
    Await Task.Delay(1000)
  End Function
End Module

This happens to be the Run() method's second overload. Here they are in their entirety:

1. Run(action As Action)
2. Run(action As Func(Of Task))
3. Run(Of TResult)(action As Func(Of Task(Of TResult))) As TResult
4. Run(Of TResult)(action As Func(Of TResult)) As TResult
InteXX
  • 6,135
  • 6
  • 43
  • 80