Hi Garry,
another async issue, found while porting the report screens of a database app. Execution silently stops after an Await inside a Select Case body: no exception, and the Finally block of the enclosing Try doesn't run.
Setup
A PopupMenu SelectionChanged handler calls an Async Sub (plain call, not awaited). The method is guarded by a Boolean that is reset in Finally:
If _loadingReport Then Return
_loadingReport = True
Try
Var data As DBAnswer = Nothing
Var rep As BarReport = Nothing
Select Case cbReport.SelectedRowIndex
Case 1
data = Await GetTotalByColorByMonth(year, lack) # works
...
Case 7
Print("7: before Await")
rep = Await CreateBarData1(year, lack) # Async Function As BarReport
Print("7: after Await")
Else
data = Await GetInspection(Nothing, lack) # works
End Select
Print("after Select")
...
Catch ex As Exception
Print("LRA exception: " + ex.GetType() + ": " + ex.Message)
Finally
Print("finally, resetting guard")
_loadingReport = False
End Try
Print("LoadReportAsync done")
End Sub
Observed output for case 7
LRA start, guard=False
7: before Await
CBD1 start
CBD1 end of Try
CBD1 returning
7: after Await
...and nothing more. No "after Select", no exception (even with Catch ex As Exception), no "finally". On the next selection the guard is still True, so every later call returns immediately and the app looks frozen.
For the other cases (1-6, Else), which also Await inside their Case body, the output is complete:
after Select
finally, resetting guard
LoadReportAsync done
The awaited function itself completes normally (the "CBD1" lines are printed inside it; it contains a Try/Catch with an Await inside).
Workaround
Moving the Await out of the Case body fixes it. The case only sets a number, and the chart is loaded after End
objo
Select:
Case 7
chartNo = 7
End Select
If chartNo > 0 Then
Var rep As BarReport = Await LoadChartReport(chartNo, year, lack)
...
End If
With that, case 7 prints "after Select", "finally" and "done" like the others.
I haven't yet reduced this to a minimal standalone project, and I can't tell exactly what distinguishes case 7 from the working cases (it assigns a class instance returned by an Async Function that itself contains Try/Catch around Awaits; the working cases assign a DBAnswer from functions without Try). Happy to send the full project or build a small repro if that helps.
Related observations (possibly the same root cause)
- Return inside a Try with Finally in an Async Sub, after an Await, appeared to leave the method without running Finally (the same guard stayed True). Removing the Return fixed it.
- The Async/Await docs show Var task As Task(Of String) = GetName() to start a task without awaiting. For me, Var t1 As Task(Of DBAnswer) = client.GetRecordAsync(...) fails with "Cannot assign 'DBAnswer' to variable of type 'Task(Of DBAnswer)'".
- The docs' fire-and-forget discard _ = SomeAsyncSub() fails with "Undefined identifier '_'". A plain call SomeAsyncSub() works.
Thanks again for the quick turnaround on the other reports!
Best regards,
Guido