An async function runs synchronously to its first await, then suspends
The common read: calling an async function defers its whole body; nothing inside it runs until the event loop runs it later.
The belief is reasonable: you write async on a function, and it is tempting to read that as "this whole function runs later, separately from my code." That reading is wrong. Calling a() pushes a frame onto your call stack, the same stack a plain function call would use, and everything in a() runs on that stack like ordinary code, up to the first await.
At await, the function suspends: the runtime pops the function's frame off the stack, and the code after the await becomes a continuation. null counts as an already-settled value, so this continuation goes straight into the microtask queue from lesson 3. The function has not finished. It is paused in the middle of its body, and the rest of it runs once the stack is empty and the event loop runs the microtask queue.
So a1 logs before main, because a() is an ordinary call that runs on your stack. And a2 logs after main, because the await moved the rest of a() into the queue, and the queue only runs once the stack is empty. The rule is that an async function runs synchronously until its first await, then suspends.

