Are all Cmd's despatched before the next update?

Can I rely on all the Cmds that I return from update being dispatched (not neccessarily completed) before update is called again?

Specifically I have a situation like this…

  • Application state is persisted both in localStorage and in the cloud, so as to allow syncing between devices.
  • Therefore my application needs to compare version data between what is stored in the localStorage and what is stored in the cloud.
  • Cloud storage is accessed via a simple API using Http commands, but requires an access key that occasionally changes.
  • To prevent always asking for the access key, it is stored in localStorage together with the state data, so that it only needs to be requested from the user when it has changed.

Thus when the access key has changed, my application flow looks like this:

Pass 1

update receives the state stored in localStorage and discovers the access key is out of date.

view provides an input box for the user to enter the new access key.

Pass 2

update receives the new access key from the user and dispatches two batched Cmds. One to fetch stored state from the cloud API using the new access key, the other to update the currently stored state in localStorage with the new access key.

Pass 3

update receives a reply from the cloud API with the application state stored there. It compares the version number of this state with that retrieved from the localStorage and (a) if it is more recent dispatches another Cmd to update localStorage with the state retrieved from the cloud or (b) if it is less recent dispatches a Cmd to update the cloud storage with the more recent state.

The Concern

Is it possible that Pass 3 commences before all the Cmds from Pass 2 have been dispatched? That is: could it happen that the Cmd to the cloud API is dispatched and triggers a new Msg before the Cmd to localStorage from Pass 2 is dispatched, and that this latter Cmd then just gets batched together with the next set of Cmds from Pass 3? (In which case there would be a race condition as to which update is the last to get written to localStorage) Or is it always guaranteed (and will continue to be in future versions) that Cmds from one update call will all be dispatched before calling update again?

Obvious workaround

I realise that in the case presented, a race condition (were it possible) could easily be avoided by storing the access key and application state in separate fields of the localStorage. However, I envision there must be situations where such a workaround would be much more difficult. So, having thought of the possibility, wanted to find out whether there are any guarantees about Cmd dispatch.

Cmds are executed in the order they are returned from the update function batchwise (order isn’t guaranteed within a batch). It isn’t possible for a Cmd in pass 3 to start working before a Cmd in pass 2 has started working.

This isn’t true of Cmd completion in the general case. Without knowing what Cmds do we can’t say for certain that a Cmd from pass 2 will always finish its work before one from pass 3 finishes it’s work. You could start an HTTP request in pass 2 and not see its result in your update until after a request has completed from pass 3. However, the localStorage API is synchronous as is the port invocation that calls it, and request completion is always asynchronous. In this specific case there will always be at least one tick between the two outcomes so there is no race condition.

These kinds of implementation details aren’t something your program’s behavior should depend on. There are no guarantees about how timing will work in future versions. If something in your program needs to happen in a particular order then you should use types and values to prove to your program that step n is finished before proceeding to step n+1. For example, you might use a subscription port to to send a message to your program that a particular version of the state is correctly saved in localStorage. Then you can make the API request within that message’s branch rather than at the same time.

4 Likes

Thanks for such an accurate and in depth reply @luke. Exactly what I was wondering :slight_smile:

1 Like

slightly off topic (sorry) but i think related to discuss. should more things in Elm be Task so we can have semantics to express the above. this question is in light of the concern that even Http is going to lose task

UPDATE: forgot to link Using Task to send HTTP requests

This topic was automatically closed 10 days after the last reply. New replies are no longer allowed.