The update function is not where you run your tasks. Think of your update function as just a way to transform your state into a new state. If you imagine your state as one big JSON object, then your update function just takes a JSON object and produces another one. That's it.
The thing with the update function is that it takes an input or action. So, the trick is that you have a mailbox of actions and you have a task send some values to that mailbox's address. This will update the mailbox's signal with a new value and this is where you get your actions for the update.
So:
Task gets performed -> Task returns with a result -> Task sends result to mailbox address -> Mailbox signal updates -> rinse and repeat
This explains you go from tasks to actions, but how do you actually perform those tasks?
- Create a mailbox for the task you want to perform
myTaskMailbox : Mailbox (Task error value)
myTaskMailbox = mailbox myTask
This will allow you to send tasks to this mailbox. When tasks get sent to the mailbox, provided a port was opened, the task will be performed.
2.
Open a port with the signal from the task mailbox
port myTaskPort : Signal (Task error value)
port myTaskPort =
myTaskMailbox.signal
If you don't include this, the tasks won't get performed. This basically tells Elm that you're trying to do something effectful or impure (like making an HTTP request or getting data from a database).
This is exactly what happens when you try to talk to JS. From Elm's perspective, JS is like this black box. The relationship between Elm and JS is basically the same as that between a browser and a server. Communicating with one another requires effectful/impure computations and the only thing they exchange with one another is data. This is why they both share the port syntax. From Elm's point of view, JS is just one possible source of effects among many.
The cool thing about this is that if you comment out the port, the tasks won't perform. The guarantee Elm provides is that ports declare which effects you're performing. No ports, no effects, just pure computation.
3. Send tasks to this mailbox.
You can send tasks from many places:
- When you create a mailbox, you get to send a task when the program starts as shown above.
- You can send tasks from within tasks.
- You can send multiple values from multiple sources to multiple addresses from within a single larger task where each intermediate task in interspersed by sleep calls or just wait for a task to complete. For example, a single task can : Fetch data from a server, parse that data, send the parsed data to an address, wait for 1 second, fetch data from a database, perform different tasks based on the result, etc... You can get quite involved with the tasks logic, except that tasks on their own are not that useful. If you want to actually change your application state, you have to send values to an address.
- You can send tasks from an event handler (like Html.Event). Use this feature carefully, but the gist of it is that you can have a button trigger an HTTP request. (onClick myTaskMailbox.address myAwesomeHttpRequest). So, basically, you can send tasks from your view. Again, if that port wasn't opened, the task won't get actually performed.
It's not quite dynamic in the sense that you send tasks based on events, but it does have to send multiple http requests. Basically, it sends a request to fetch a JSON file to that contains the locations of the articles to display and then perform all of those requests. If you scroll down, there's a write-up/tutorial that even explains how to get the requests to load in parallel.
The second example is an artist search example.
https://gist.github.com/TheSeamau5/98527f7278a2c6f06092This one doesn't have an update function but does a lot of task stuff. This should kinda give you the idea that tasks really don't go in the update function.
This one performs tasks in event handlers and when the program starts. If the page fails to load, you get a button to retrigger the loading of the page. So, you're sending a task from the button.
I hope these examples help a bit. Tasks are still new in Elm, so the best practices haven't fully formed yet, but, to some up, the idea is this.
Create a mailbox for your tasks.
Open a port with the signal of the task mailbox.
Send your tasks to this mailbox.
You can send tasks when the program starts, from within other tasks, or from event handlers.
Send a value to an address is a task, so use tasks to send data to other mailboxes, i.e. mailboxes for actions, not tasks.
Update deals exclusively with actions and state. It is a pure function that given an action and a current state produces the next state.
I hope this helps,
Hassan