Async procedures & Await
Keep the interface responsive while respecting McBasic™’s deliberately bounded asynchronous syntax.
Start from an allowed entry point#
Use a parameterless Async Sub for a supported ordinary Form or control event, such as a button Click or Form_Shown. An async-capable control-array event additionally takes a leading explicit ByVal Index As Integer; see shared array handlers. Move reusable work into a Private Async Sub for an action or an Async Function for a result, and Await that helper from each caller. Do not call an Async event handler like an ordinary synchronous routine. Form_Load and Form_Initialize are not asynchronous entry points. Neither are Form_Closing, Form_QueryUnload or Form_Unload.
The other supported parameterless Form entry points include Activate, Deactivate, Resize and Closed. An event with parameters cannot be turned into Async merely by adding the keyword.
Await owns the entire operation#
| Pattern | Meaning |
|---|---|
Await SaveAsync() | Wait for an operation without taking its result. |
count = Await CountAsync() | Assign the awaited value. |
Set db = Await Sqlite.OpenAsync(path) | Assign the awaited object reference. |
Await is a statement or the whole right side of an assignment. Do not place it inside another expression. Store the awaited value first, then transform it on the next line. Calls need parentheses and positional arguments.
Declare a supported asynchronous contract#
Write ByVal explicitly on every Async Function parameter. ByRef, Optional, ParamArray and array parameters are unavailable. Results may be supported value types or built-in objects such as Database and Recordset, but not arrays, enums, Forms or user classes.
A real startup sequence#
This handler belongs to DataWorkbench. Its declarations, SetBusy helper, RefreshRows helper and controls are all included in the complete lesson. It opens the connection, migrates the schema and only then enables actions.
Private Async Sub Form_Shown()
Dim migrations As MigrationSet
Dim schema As DbSchema
Dim version As Long
mBusy = True
Try
Set mDb = Await Sqlite.OpenAsync(AppPaths.DataFile("data-workbench.db"))
Set migrations = Sqlite.CreateMigrationSet()
migrations.Add 1, "CREATE TABLE Accounts (ID INTEGER PRIMARY KEY, Name TEXT NOT NULL, Balance INTEGER NOT NULL CHECK(Balance >= 0)); INSERT INTO Accounts VALUES(1, 'First', 100); INSERT INTO Accounts VALUES(2, 'Second', 100);"
version = Await mDb.ApplyMigrationsAsync(migrations)
Set schema = Await mDb.GetSchemaAsync()
If Not schema.HasTable("Accounts") Then Err.Raise 513, "DataWorkbench", "Accounts table missing."
Await RefreshRows()
SetBusy False
lblStatus.Caption = "Migration " & CStr(version) & "; " & CStr(schema.TableCount) & " table(s). Transfers preserve a total of 200 units."
Catch failure As McException
lblStatus.Caption = "Startup failed: " & failure.Message
mBusy = False
End Try
End SubTreat re-entry and closure as separate decisions#
Await yields while work is pending; another event can arrive. Use a busy flag and disable conflicting actions before the first Await. Restore the interface when the operation finishes. A close handler may veto closure synchronously while a cancellable operation winds down; do not make a forbidden close event Async to force the issue.
Cancellation is a request, not proof that nothing happened. After cancelling a database write, requery before telling the user whether it took effect. Use the complete CustomerDesk and download workflows for those boundaries.
20f4bfed6b · Build manifestSources & maintenance
Original web explanation, reviewed against these repository sources. API tables and example files follow the implementation.
docs/manual/21-language-reference.mddocs/manual/11-language-toolkit.mdsite/examples/LanguageWorkbench/Main.bassite/examples/DataWorkbench/frmMain.basdocs/manual/12-sqlite-and-data.md