Saga pattern & redux-saga

Jean Pan
Jean Pan
Oct 23, 2016 · 3 min read

This article is my understanding of saga pattern & redux-saga. Please feel free to let me know if there is anything wrong.

http://kotaku.com/adventure-time-explained-1703199261

Saga Pattern

The term saga was introduced by Hector Garcia-Molina to define a mechanism to handle system failures in long lived transaction.

From Clarifying the Saga Pattern: A Saga is a distribution of long-living transactions where steps may interleave, each with associated compensating transactions providing a compensation path across databases in the occurrence of a fault that may or may not compensate the entire chain back to the originator.

http://kellabyte.com/2012/05/30/clarifying-the-saga-pattern/

Above figure shows saga compensating due to a failed transaction (T4).

If all the transactions succeed: T1 — T2 — T3 … — Tn but if Tn failed : T1 — T2 — T3 … — Tn — Cn — C(n-1)… — C1

Watch this talk to learn more about saga pattern.

redux-saga

redux-saga is a redux middleware for handling side effects.

How does redux-saga work ?

redux-saga is implemented with generator functions. Generator functions can be paused and resumed and yield multiple values.

To understand how redux-saga works, let’s see the example directly and compare with redux-thunk.

Example : Request data from a url when an UI component is clicked

redux-thunk

function fetchUrl(url) {
return (dispatch) => {
dispatch({
type: 'FETCH_REQUEST'
});

fetch(url).then(data => dispatch({
type: 'FETCH_SUCCESS',
data
}));
}
}

Dispatch fetchUrl when UI component is clicked.

dispatch(
fetchUrl(url)
);

To avoid React components being pollute with business logic, redux-thunk is the simplest way to do that, However, it may have some problem …

  • Difficult to test : to test the task logic, need to mock all invoked functions.
  • Difficult to coordinate concurrent tasks : how to cancel the pending fetch ?
  • Scattered business logic

redux-saga

UI components dispatch plain object actions instead invoking the tasks directly.

dispatch({
type: 'FETCH_REQUEST',
url: /* ... */}
);

Create a saga that watches for the dispatched action and forks the task.

import { take, fork, call, put } from 'redux-saga/effects';

// The watcher: watch actions and coordinate worker tasks
function* watchFetchRequests() {
while(true) {
const action = yield take('FETCH_REQUEST');

yield fork(fetchUrl, action.url);
}
}

// The worker: perform the requested task
function* fetchUrl(url) {
const data = yield call(fetch, url);

yield put({
type: 'FETCH_SUCCESS',
data
});
}

Note all function from redux-saga is pure functions and yields plain Javascript object called effect. For example, call(fetchUrl, url) returns { type: CALL, function: fetchUrl, args: [url] }. The effects are created through functions but are executed in the middleware.

The separation between effect creation and execution makes testing simple.

const generator = fetchUrl('http://api');const expected = call(fetch, 'http://api');
const actual = generator.next().value;
assert.deepEqual(expected, actual);

Because of generators, we have much more flexibility to coordinate concurrent tasks.

import { take, fork, cancel, call, put } from 'redux-saga/effects';// cancel any pending fetch whenever request a new fetch
function* watchFetchRequests() {
let currentTask;

while(true) {
const action = yield take('FETCH_REQUEST');

if(currentTask) {
yield cancel(currentTask);
}

currentTask = yield fork(fetchUrl, action.url);
}
}

Why redux-saga ?

  • Declarative effects: all operations in redux-saga yield plain Javascript objects, which makes it easier to test the business logic.
  • Advanced async control flow : simply describe async flow with sync style and familiar control flow constructs.
  • Concurrency management : provide primitives and operators to manage concurrency between tasks; able to fork multiple background tasks in parallel and cancel a running task.
  • Side effect handler : all side effects are moved into sagas, UI components do not perform any business logic but only dispatch actions to notify what happened.

Conclusion

Saga pattern is a design pattern to handle system failures in long lived transaction by dividing the long lived transaction into a set of independent sub-transactions with compensating sub-transactions.

redux-saga is a redux middleware for handling side effects which make React Redux App more testable, maintainable, and reusable.

Reference

Jean Pan

Written by

Jean Pan

👩🏻‍💻

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade