The exact sequence is something like this:
-
update
returnsmodel
and a batch of commands - an animation-frame is requested with the diffing and rendering of the view as its callback
- commands and subscriptions are “distributed” to their effect managers
This ordering has an interesting property: if a command (like an HTTP request) doesn’t care about the view being rendered first, it won’t “wait” for that to happen. However, if a command (like Dom.focus
) does care, it can wait for the view to be rendered rather simply: all it needs to do is wrap itself in requestAnimationFrame
.
The trick is that calling requestAnimationFrame
multiple times with different callbacks will ensure that the callback are also executed in the order in which they were queued. Since the view
is queued before commands begin executing, commands can use this trick to ensure they run after view-rendering.
Libraries like elm-lang/dom
already use this trick, so when you return ( { model | renderMyDiv = True }, Task.attempt FocusResult (Dom.focus "input-in-my-div") )
for example, the view
is rendered before focussing is actually attempted.
When you’re dealing with ports
, you can use a similar trick. On the Elm side, nothing changes - you simply call your port. On the JS side, you can do something like this:
elm.app.ports.foo.subscribe(function (id) {
requestAnimationFrame(function () {
/* when this callback executes, the view should have rendered. */
})
})