Should work just fine on the IC, canister calls are asynchronous.
E.g. if you have one incoming ingress message that executes the code above, the sequence of events is the following:
- Ingress message is executed as transaction. It enqueues N outgoing canister requests into its output queues and finishes execution when it hits the
await
(persisting any local variables, call stack, etc. into a call context; and reserving a callback referring to this call context for each of the enqueued requests). (One transaction) - Each of the requests is routed to whatever destination canister and executed, producing a response. (N transactions)
- Each response is routed to your original canister and each is executed as a separate message, updating the call context created at step (1) (e.g. by marking the respective future under
join_all()
asReady
) and dropping the corresponding callback. (N transactions) - (Actually 3b) When the last response at step (3) is executed, all callbacks have been dropped, the
await
returns and execution of the code beyond it continues, eventually producing a reply to the ingress message and dropping the call context. (This the Nth transaction from step (3) above, not a separate transaction.)
All in all, you have N+1 transactions executed on your canister (the ingress message plus N responses) and N transactions executed on downstream canisters (each request you’ve sent, assuming these complete immediately and don’t result in further downstream messages). It’s just a whole lot of messages being passed around and executed asynchronously (except that all messages executed on a given canister are executed sequentially – since canisters are single threaded – but in no particular order).