Effectful State Management: Asynchronous Effects

Episode #78 • Oct 28, 2019 • Subscriber-Only

It’s time to finish our architecture’s story for side effects. We’ve described synchronous effects and unidirectional effects, but we still haven’t captured the complexity of async effects. Let’s fix that with a final, functional refactor.

Previous episode
Effectful State Management: Asynchronous Effects
Next episode
Locked

Unlock This Episode

Our Free plan includes 1 subscriber-only episode of your choice, plus weekly updates from our newsletter.

Sign in with GitHub

Introduction

So, one more effect has been extracted. Let’s again take a moment to reflect on what we have accomplished.

We wanted to extract the loading from disk effect out of our view and somehow model it in the reducer. We quickly realized that this effect was not quite like the previous effect we handled. The save effect was essentially fire-and-forget, it just did its work and didn’t need to notify anyone of anything after.

However, the loading effect needed to somehow feed its loaded data back into the reducer so that we could react. This led us to refactoring the effecting signature from being a void-to-void closure to being a void-to-optional action closure. This allows effects to do the bare minimum of work necessary to get the job done, and then feed the result back into the reducer by sending another action. Then the store becomes the interpreter of these effects by first running the reducer, collecting all of the effects that want to be executed, iterating over that error to execute the effects, and then sending any actions the effects produced back into the store.

This right here is what people refer to when they say “unidirectional data flow.” Data is only ever mutated in one single way: an action comes into the reducer which allows the reducer to mutate the state. If you want to mutate the state via some side effect work, you have no choice but to construct a new action that can then be fed back into the reducer, which only then gives you the ability to mutate.

This kind of data flow is super understandable because you only have one place to look for how state can be mutated, but it also comes at the cost of needing to add extra actions to take care of feeding effect results back into the reducer. This is why many UI frameworks, SwiftUI included, give ways to sidestep the strict unidirectional style in order to simplify usage, as they do with two-way bindings, but this can be at the cost of complicating how data flows through the UI.

Extracting our asynchronous effect


References

  • Elm: Commands and Subscriptions

    Elm is a pure functional language wherein applications are described exclusively with unidirectional data flow. It also has a story for side effects that closely matches the approach we take in these episodes. This document describes how commands (like our effect functions) allow for communication with the outside world, and how the results can be mapped into an action (what Elm calls a “message”) in order to be fed back to the reducer.

  • Redux: Data Flow

    The Redux documentation describes and motivates its “strict unidirectional data flow.”

  • Redux Middleware

    Redux, at its core, is very simple and has no single, strong opinion on how to handle side effects. It does, however, provide a means of layering what it calls “middleware” over reducers, and this third-party extension point allows folks to adopt a variety of solutions to the side effect problem.

  • Redux Thunk

    Redux Thunk is the recommended middleware for basic Redux side effects logic. Side effects are captured in “thunks” (closures) to be executed by the store. Thunks may optionally utilize a callback argument that can feed actions back to the store at a later time.

  • ReSwift

    ReSwift is one of the earliest, most popular Redux-inspired libraries for Swift. Its design matches Redux, including its adoption of “middleware” as the primary means of introducing side effects into a reducer.

  • SwiftUIFlux
    Thomas Ricouard

    An early example of Redux in SwiftUI. Like ReSwift, it uses “middleware” to handle side effects.

  • Elm: A delightful language for reliable webapps

    Elm is both a pure functional language and framework for creating web applications in a declarative fashion. It was instrumental in pushing functional programming ideas into the mainstream, and demonstrating how an application could be represented by a simple pure function from state and actions to state.

  • Redux: A predictable state container for JavaScript apps.

    The idea of modeling an application’s architecture on simple reducer functions was popularized by Redux, a state management library for React, which in turn took a lot of inspiration from Elm.

  • Composable Reducers
    Brandon Williams • Oct 10, 2017

    A talk that Brandon gave at the 2017 Functional Swift conference in Berlin. The talk contains a brief account of many of the ideas covered in our series of episodes on “Composable State Management”.

Downloads

Get started with our free plan

Our free plan includes 1 subscriber-only episode of your choice, access to 62 free episodes with transcripts and code samples, and weekly updates from our newsletter.

View plans and pricing