Here's my example code. It's of no use, but it's short.
I have an array to switch on or off the creation of Exceptions at different parts of my code and two functions. The first (Level 0) is an iterator function to deliver all the data (for instance reading all the files in a directory), and the second one (Level 1) is a function to deliver the data for one specific item (for instance reading and interpreting exactly one file).
Private _errorOnLevel As Boolean() = {False, False}
Private Iterator Function GetAllData() As IEnumerable(Of Integer)
If _errorOnLevel(0) Then
Throw New Exception("Error in GetAllData")
End If
Yield GetSingleData(1)
Yield GetSingleData(2)
Yield GetSingleData(3)
End Function
Private Function GetSingleData(value As Integer) As Integer
If _errorOnLevel(1) Then
Throw New Exception("Error in GetSingleData")
End If
Return value
End Function
I want to get all the data inside of the constructor of a view model, and because the real code needs a little bit of time to read and process all the data, I want to exceute the code asyncronously so that the window can render itself without waiting for all the data to be read and processed.
Async/Await can't really be used in a constructor of a class (so I read), so I tried a different approach (example code is for a console program):
Public Sub Main()
Dim t1 As Task = Task.Factory.
StartNew(Of IEnumerable(Of Integer))(AddressOf GetAllData).
ContinueWith(
Sub(t2 As Task(Of IEnumerable(Of Integer)))
For Each item As Integer In t2.Result
Console.WriteLine(item)
Next item
End Sub)
End Sub
This works as long as there are no errors. But when "activating" my errors, what do I need to do to catch those errors to be able to react to it?
Putting the whole Dim t1 as Task code inside a Try/Catch does nothing.
Adding a task continuation with TaskContinuationOptions.OnlyOnFaulted in the middle or at the end of my task chain does nothing.
Dim t1 As Task = Task.Factory.
StartNew(Of IEnumerable(Of Integer))(AddressOf GetAllData).
ContinueWith(
Sub(t2 As Task(Of IEnumerable(Of Integer)))
Console.WriteLine("Error in Task 1")
End Sub, TaskContinuationOptions.OnlyOnFaulted).
ContinueWith(
Sub(t3 As Task(Of IEnumerable(Of Integer)))
For Each item As Integer In t3.Result
Console.WriteLine(item)
Next item
End Sub)
The only solution I found so far is to put a Try/Catch inside the continuation task and returning a possible exception and reacting to this result in another continuation.
Dim t1 As Task = Task.Factory.
StartNew(Of IEnumerable(Of Integer))(AddressOf GetAllData).
ContinueWith(Of Exception)(
Function(t2 As Task(Of IEnumerable(Of Integer))) As Exception
Try
For Each item As Integer In t2.Result
Console.WriteLine(item)
Next item
Return Nothing
Catch ex As Exception
Return ex
End Try
End Function).
ContinueWith(
Sub(t3 As Task(Of Exception))
If t3.Result IsNot Nothing Then
Console.WriteLine(t3.Result.Message)
End If
End Sub)
But is that really the way to go?
used in a constructor of a classthen don't do this in a constructor. A constructor should return a viable object. Sure, you can hide the tasks withTask.Runbut the object will be incomplete until laterasyncconstructor but that doesn't mean it can't call anasync Taskmethod and store the returned task in a field, egMyCrawler(string folder){ _crawlTask=Task.Run(()=>Crawl(folder)); }the constructor of a view modelthat's definitely not the place to load data in MVVM. A ViewModel contains the results of queries, typically triggered by commands (if the MVVM framework supports them) or UI events.