Here’s my observation on Bubble’s workflow synchronicity …
If custom events are scheduled in a workflow step, then they run asynchronously to the current workflow.
If custom events are invoked in a workflow step, then their steps are as synchronous as normal steps.
In general, normal steps are asynchronous (but initiated in order) unless Bubble sees a connection between them, like using a previous step’s result, or using a custom state value that is set in a previous step, or using a database field in an expression where the field was set in a previous step (it was really messy before this last one was added!).
What is missing is connecting plugin elements in this checking, i.e. if a plugin is altered in one step, a future step should wait for the plugin to finish its update before using its value.
Then, in general, the workflow running on the server is asynchronous with the workflow running on the client, but there are synchronising conditions where the server will wait for the client to catch up, like for example, setting an element’s value to a database field.
@josh has posted some info about this, but it really does deserve a clear section in the Manual.