Linking Tasks
Motivation
Breaking up asynchronous calls into bitesize chunks makes a lot of sense: it’s easier to reason about, it allows us to show different states depending on what stage of the call we are in, and it helps us organize our code better.
But the biggest issue this exposes that it is very difficult to cancel all children if the parent is cancelled.
Imagine this scenario: we’re creating a blog feed widget that will get all of the user’s posts and comments and show them in a small box somewhere on the page. We might have something like this to load all of those posts and comments:
Now this is great, it’s been neatly enclosed in a component so that the rest of the page doesn’t need to be blocked while waiting for this widget to load. We could also include some logic to enable us to show which call is currently being run.
But what would happen if the user navigated away or destroyed between the initial call and the final result? Our API would still receive those requests and go and do all of the work to get the data and return it but we are no longer using the returned data or even have a reference to it in our app. This kind of scenario can very easily cause data leaks that can gradually bring our app down.
If only there was a way to link these calls together.
Solution
Well, we heard your cries and created the link
function.
Link
is one of two SheepdogUtils and it enables us to link a child task to its parent so that the lifecycle of the child task is directly bound to the lifecycle of its parent. That means that if the parent is canceled, the child is automatically aborted.
Turning our above scenario into tasks would look like this:
And now all of our child tasks are bound to the parent context, meaning if the context that the parent task lives on is destroyed, all of the other tasks will be cancelled.
Another added benefit of this is that we already have the different loading states out of the box, we could simply see which task is running and be able to show each loading state individually.