I think your overthinking it. Data sent over ports is non-blocking by default - there is no response to wait for, so your JS calculation can do what it wants without affecting Elm.
Why do you need to know?
I use ports and Ajax to upload files with a progress bar in Elm.
The data sent from JS to Elm over the port is fast, so you don’t need to wait for it to render inside your JS calculation before doing anything.
You could just do something like this inside your calculate function:
elmapp.ports.calculationProgress.send(i)
Then in Elm your subscriptions could include a line like:
calculationProgress (\ progress -> ProgressMsg progress)
Your Msg definition could be something like:
type Msg
= ...
| ProgessMsg Int
...
And then you can update the progress in your model, and your view will render with the updated progress.