API Reference

Cheatsheets

Middleware API

createSagaMiddleware(options)

Creates a Redux middleware and connects the Sagas to the Redux Store

Example

Below we will create a function configureStore which will enhance the Store with a new method runSaga. Then in our main module, we will use the method to start the root Saga of the application.

configureStore.js

import createSagaMiddleware from 'redux-saga'
import reducer from './path/to/reducer'

export default function configureStore(initialState) {
  // Note: passing middleware as the last argument to createStore requires redux@>=3.1.0
  const sagaMiddleware = createSagaMiddleware()
  return {
    ...createStore(reducer, initialState, applyMiddleware(/* other middleware, */sagaMiddleware)),
    runSaga: sagaMiddleware.run
  }
}

main.js

import configureStore from './configureStore'
import rootSaga from './sagas'
// ... other imports

const store = configureStore()
store.runSaga(rootSaga)

Notes

See below for more information on the sagaMiddleware.run method.

middleware.run(saga, ...args)

Dynamically run saga. Can be used to run Sagas only after the applyMiddleware phase.

The method returns a Task descriptor.

Notes

saga must be a function which returns a Generator Object. The middleware will then iterate over the Generator and execute all yielded Effects.

saga may also start other sagas using the various Effects provided by the library. The iteration process described below is also applied to all child sagas.

In the first iteration, the middleware invokes the next() method to retrieve the next Effect. The middleware then executes the yielded Effect as specified by the Effects API below. Meanwhile, the Generator will be suspended until the effect execution terminates. Upon receiving the result of the execution, the middleware calls next(result) on the Generator passing it the retrieved result as an argument. This process is repeated until the Generator terminates normally or by throwing some error.

If the execution results in an error (as specified by each Effect creator) then the throw(error) method of the Generator is called instead. If the Generator function defines a try/catch surrounding the current yield instruction, then the catch block will be invoked by the underlying Generator runtime. The runtime will also invoke any corresponding finally block.

In the case a Saga is cancelled (either manually or using the provided Effects), the middleware will invoke return() method of the Generator. This will cause the Generator to skip directly to the finally block.

Effect creators

Notes:

take(pattern)

Creates an Effect description that instructs the middleware to wait for a specified action on the Store.
The Generator is suspended until an action that matches pattern is dispatched.

The result of yield take(pattern) is an action object being dispatched.

pattern is interpreted using the following rules:

Note: if the pattern function has toString defined on it, action.type will be tested against pattern.toString() instead. This is useful if you're using an action creator library like redux-act or redux-actions.

The middleware provides a special action END. If you dispatch the END action, then all Sagas blocked on a take Effect will be terminated regardless of the specified pattern. If the terminated Saga has still some forked tasks which are still running, it will wait for all the child tasks to terminate before terminating the Task.

takeMaybe(pattern)

Same as take(pattern) but does not automatically terminate the Saga on an END action. Instead all Sagas blocked on a take Effect will get the END object.

Notes

takeMaybe got its name from the FP analogy - it's like instead of having a return type of ACTION (with automatic handling) we can have a type of Maybe(ACTION) so we can handle both cases:

take(channel)

Creates an Effect description that instructs the middleware to wait for a specified message from the provided Channel. If the channel is already closed, then the Generator will immediately terminate following the same process described above for take(pattern).

takeMaybe(channel)

Same as take(channel) but does not automatically terminate the Saga on an END action. Instead all Sagas blocked on a take Effect will get the END object. See more here

takeEvery(pattern, saga, ...args)

Spawns a saga on each action dispatched to the Store that matches pattern.

Example

In the following example, we create a basic task fetchUser. We use takeEvery to start a new fetchUser task on each dispatched USER_REQUESTED action:

import { takeEvery } from `redux-saga/effects`

function* fetchUser(action) {
  ...
}

function* watchFetchUser() {
  yield takeEvery('USER_REQUESTED', fetchUser)
}

Notes

takeEvery is a high-level API built using take and fork. Here is how the helper could be implemented using the low-level Effects

const takeEvery = (patternOrChannel, saga, ...args) => fork(function*() {
  while (true) {
    const action = yield take(patternOrChannel)
    yield fork(saga, ...args.concat(action))
  }
})

takeEvery allows concurrent actions to be handled. In the example above, when a USER_REQUESTED
action is dispatched, a new fetchUser task is started even if a previous fetchUser is still pending
(for example, the user clicks on a Load User button 2 consecutive times at a rapid rate, the 2nd
click will dispatch a USER_REQUESTED action while the fetchUser fired on the first one hasn't yet terminated)

takeEvery doesn't handle out of order responses from tasks. There is no guarantee that the tasks will
terminate in the same order they were started. To handle out of order responses, you may consider takeLatest
below.

takeEvery(channel, saga, ...args)

You can also pass in a channel as argument and the behaviour is the same as takeEvery(pattern, saga, …args).

takeLatest(pattern, saga, ...args)

Spawns a saga on each action dispatched to the Store that matches pattern. And automatically cancels
any previous saga task started previously if it's still running.

Each time an action is dispatched to the store. And if this action matches pattern, takeLatest
starts a new saga task in the background. If a saga task was started previously (on the last action dispatched
before the actual action), and if this task is still running, the task will be cancelled.

Example

In the following example, we create a basic task fetchUser. We use takeLatest to
start a new fetchUser task on each dispatched USER_REQUESTED action. Since takeLatest
cancels any pending task started previously, we ensure that if a user triggers multiple consecutive
USER_REQUESTED actions rapidly, we'll only conclude with the latest action

import { takeLatest } from `redux-saga/effects`

function* fetchUser(action) {
  ...
}

function* watchLastFetchUser() {
  yield takeLatest('USER_REQUESTED', fetchUser)
}

Notes

takeLatest is a high-level API built using take and fork. Here is how the helper could be implemented using the low-level Effects

const takeLatest = (patternOrChannel, saga, ...args) => fork(function*() {
  let lastTask
  while (true) {
    const action = yield take(patternOrChannel)
    if (lastTask) {
      yield cancel(lastTask) // cancel is no-op if the task has already terminated
    }
    lastTask = yield fork(saga, ...args.concat(action))
  }
})

takeLatest(channel, saga, ...args)

You can also pass in a channel as argument and the behaviour is the same as takeLatest(pattern, saga, …args).

takeLeading(pattern, saga, ...args)

Spawns a saga on each action dispatched to the Store that matches pattern.
After spawning a task once, it blocks until spawned saga completes and then starts to listen for a pattern again.

In short, takeLeading is listening for the actions when it doesn't run a saga.

Example

In the following example, we create a basic task fetchUser. We use takeLeading to
start a new fetchUser task on each dispatched USER_REQUESTED action. Since takeLeading
ignores any new coming task after it's started, we ensure that if a user triggers multiple consecutive
USER_REQUESTED actions rapidly, we'll only keep on running with the leading action

import { takeLeading } from `redux-saga/effects`

function* fetchUser(action) {
  ...
}

function* watchLastFetchUser() {
  yield takeLeading('USER_REQUESTED', fetchUser)
}

Notes

takeLeading is a high-level API built using take and call. Here is how the helper could be implemented using the low-level Effects

const takeLeading = (patternOrChannel, saga, ...args) => fork(function*() {
  while (true) {
    const action = yield take(patternOrChannel);
    yield call(saga, ...args.concat(action));
  }
})

takeLeading(channel, saga, ...args)

You can also pass in a channel as argument and the behavior is the same as takeLeading(pattern, saga, …args).

put(action)

Creates an Effect description that instructs the middleware to dispatch an action to the Store.
This effect is non-blocking and any errors that are thrown downstream (e.g. in a reducer) will
not bubble back into the saga.

putResolve(action)

Just like put but the effect is blocking (if promise is returned from dispatch it will wait for its resolution) and will bubble up errors from downstream.

put(channel, action)

Creates an Effect description that instructs the middleware to put an action into the provided channel.

This effect is blocking if the put is not buffered but immediately consumed by takers. If an error
is thrown in any of these takers it will bubble back into the saga.

call(fn, ...args)

Creates an Effect description that instructs the middleware to call the function fn with args as arguments.

Notes

fn can be either a normal or a Generator function.

The middleware invokes the function and examines its result.

If the result is an Iterator object, the middleware will run that Generator function, just like it did with the
startup Generators (passed to the middleware on startup). The parent Generator will be
suspended until the child Generator terminates normally, in which case the parent Generator
is resumed with the value returned by the child Generator. Or until the child aborts with some
error, in which case an error will be thrown inside the parent Generator.

If fn is a normal function and returns a Promise, the middleware will suspend the Generator until the Promise is
settled. After the promise is resolved the Generator is resumed with the resolved value, or if the Promise
is rejected an error is thrown inside the Generator.

If the result is not an Iterator object nor a Promise, the middleware will immediately return that value back to the saga,
so that it can resume its execution synchronously.

When an error is thrown inside the Generator, if it has a try/catch block surrounding the
current yield instruction, the control will be passed to the catch block. Otherwise,
the Generator aborts with the raised error, and if this Generator was called by another
Generator, the error will propagate to the calling Generator.

call([context, fn], ...args)

Same as call(fn, ...args) but supports passing a this context to fn. This is useful to
invoke object methods.

call([context, fnName], ...args)

Same as call([context, fn], ...args) but supports passing a fn as string. Useful for invoking object's methods, i.e. yield call([localStorage, 'getItem'], 'redux-saga')

call({context, fn}, ...args)

Same as call([context, fn], ...args) but supports passing context and fn as properties of an object, i.e. yield call({context: localStorage, fn: localStorage.getItem}, 'redux-saga'). fn can be a string or a function.

apply(context, fn, [args])

Alias for call([context, fn], ...args).

cps(fn, ...args)

Creates an Effect description that instructs the middleware to invoke fn as a Node style function.

Notes

The middleware will perform a call fn(...arg, cb). The cb is a callback passed by the middleware to
fn. If fn terminates normally, it must call cb(null, result) to notify the middleware
of a successful result. If fn encounters some error, then it must call cb(error) in order to
notify the middleware that an error has occurred.

The middleware remains suspended until fn terminates.

cps([context, fn], ...args)

Supports passing a this context to fn (object method invocation)

cps({context, fn}, ...args)

Same as cps([context, fn], ...args) but supports passing context and fn as properties of an object. fn can be a string or a function.

fork(fn, ...args)

Creates an Effect description that instructs the middleware to perform a non-blocking call on fn

Arguments

returns a Task object.

Notes

fork, like call, can be used to invoke both normal and Generator functions. But, the calls are
non-blocking, the middleware doesn't suspend the Generator while waiting for the result of fn.
Instead as soon as fn is invoked, the Generator resumes immediately.

fork, alongside race, is a central Effect for managing concurrency between Sagas.

The result of yield fork(fn ...args) is a Task object. An object with some useful
methods and properties.

All forked tasks are attached to their parents. When the parent terminates the execution of its
own body of instructions, it will wait for all forked tasks to terminate before returning.

Error propagation

Errors from child tasks automatically bubble up to their parents. If any forked task raises an uncaught error, then
the parent task will abort with the child Error, and the whole Parent's execution tree (i.e. forked tasks + the
main task represented by the parent's body if it's still running) will be cancelled.

Cancellation of a forked Task will automatically cancel all forked tasks that are still executing. It'll
also cancel the current Effect where the cancelled task was blocked (if any).

If a forked task fails synchronously (ie: fails immediately after its execution before performing any
async operation), then no Task is returned, instead the parent will be aborted as soon as possible (since both
parent and child execute in parallel, the parent will abort as soon as it takes notice of the child failure).

To create detached forks, use spawn instead.

fork([context, fn], ...args)

Supports invoking forked functions with a this context

fork({context, fn}, ...args)

Same as fork([context, fn], ...args) but supports passing context and fn as properties of an object. fn can be a string or a function.

spawn(fn, ...args)

Same as fork(fn, ...args) but creates a detached task. A detached task remains independent from its parent and acts like
a top-level task. The parent will not wait for detached tasks to terminate before returning and all events which may affect the
parent or the detached task are completely independents (error, cancellation).

spawn([context, fn], ...args)

Supports spawning functions with a this context

join(task)

Creates an Effect description that instructs the middleware to wait for the result
of a previously forked task.

Notes

join will resolve to the same outcome of the joined task (success or error). If the joined
task is cancelled, the cancellation will also propagate to the Saga executing the join
effect. Similarly, any potential callers of those joiners will be cancelled as well.

join([...tasks])

Creates an Effect description that instructs the middleware to wait for the results of previously forked tasks.

Notes

It wraps the array of tasks in join effects, roughly becoming the equivalent of
yield tasks.map(t => join(t)).

cancel(task)

Creates an Effect description that instructs the middleware to cancel a previously forked task.

Notes

To cancel a running task, the middleware will invoke return on the underlying Generator
object. This will cancel the current Effect in the task and jump to the finally block (if defined).

Inside the finally block, you can execute any cleanup logic or dispatch some action to keep the
store in a consistent state (e.g. reset the state of a spinner to false when an ajax request
is cancelled). You can check inside the finally block if a Saga was cancelled by issuing
a yield cancelled().

Cancellation propagates downward to child sagas. When cancelling a task, the middleware will also
cancel the current Effect (where the task is currently blocked). If the current Effect
is a call to another Saga, it will be also cancelled. When cancelling a Saga, all attached forks (sagas forked using yield fork()) will be cancelled. This means that cancellation
effectively affects the whole execution tree that belongs to the cancelled task.

cancel is a non-blocking Effect. i.e. the Saga executing it will resume immediately after
performing the cancellation.

For functions which return Promise results, you can plug your own cancellation logic
by attaching a [CANCEL] to the promise.

The following example shows how to attach cancellation logic to a Promise result:

import { CANCEL } from 'redux-saga'
import { fork, cancel } from 'redux-saga/effects'

function myApi() {
  const promise = myXhr(...)

  promise[CANCEL] = () => myXhr.abort()
  return promise
}

function* mySaga() {

  const task = yield fork(myApi)

  // ... later
  // will call promise[CANCEL] on the result of myApi
  yield cancel(task)
}

redux-saga will automatically cancel jqXHR objects using their abort method.

cancel([...tasks])

Creates an Effect description that instructs the middleware to cancel previously forked tasks.

Notes

It wraps the array of tasks in cancel effects, roughly becoming the equivalent of
yield tasks.map(t => cancel(t)).

cancel()

Creates an Effect description that instructs the middleware to cancel a task in which it has been yielded (self-cancellation).
It allows to reuse destructor-like logic inside a finally blocks for both outer (cancel(task)) and self (cancel()) cancellations.

Example

function* deleteRecord({ payload }) {
  try {
    const { confirm, deny } = yield call(prompt);
    if (confirm) {
      yield put(actions.deleteRecord.confirmed())
    }
    if (deny) {
      yield cancel()
    }
  } catch(e) {
    // handle failure
  } finally {
    if (yield cancelled()) {
      // shared cancellation logic
      yield put(actions.deleteRecord.cancel(payload))
    }
  }
}

select(selector, ...args)

Creates an effect that instructs the middleware to invoke the provided selector on the
current Store's state (i.e. returns the result of selector(getState(), ...args)).

If select is called without argument (i.e. yield select()) then the effect is resolved
with the entire state (the same result of a getState() call).

It's important to note that when an action is dispatched to the store, the middleware first
forwards the action to the reducers and then notifies the Sagas. This means that when you query the
Store's State, you get the State after the action has been applied.
However, this behavior is only guaranteed if all subsequent middlewares call next(action) synchronously. If any subsequent middleware calls next(action) asynchronously (which is unusual but possible), then the sagas will get the state from before the action is applied. Therefore it is recommended to review the source of each subsequent middleware to ensure it calls next(action) synchronously, or else ensure that redux-saga is the last middleware in the call chain.

Notes

Preferably, a Saga should be autonomous and should not depend on the Store's state. This makes
it easy to modify the state implementation without affecting the Saga code. A saga should preferably
depend only on its own internal control state when possible. But sometimes, one could
find it more convenient for a Saga to query the state instead of maintaining the needed data by itself
(for example, when a Saga duplicates the logic of invoking some reducer to compute a state that was
already computed by the Store).

For example, suppose we have this state shape in our application:

state = {
  cart: {...}
}

We can create a selector, i.e. a function which knows how to extract the cart data from the State:

./selectors

export const getCart = state => state.cart

Then we can use that selector from inside a Saga using the select Effect:

./sagas.js

import { take, fork, select } from 'redux-saga/effects'
import { getCart } from './selectors'

function* checkout() {
  // query the state using the exported selector
  const cart = yield select(getCart)

  // ... call some API endpoint then dispatch a success/error action
}

export default function* rootSaga() {
  while (true) {
    yield take('CHECKOUT_REQUEST')
    yield fork(checkout)
  }
}

checkout can get the needed information directly by using select(getCart). The Saga is coupled only with the getCart selector. If we have many Sagas (or React Components) that needs to access the cart slice, they will all be coupled to the same function getCart. And if we now change the state shape, we need only to update getCart.

actionChannel(pattern, [buffer])

Creates an effect that instructs the middleware to queue the actions matching pattern using an event channel. Optionally, you can provide a buffer to control buffering of the queued actions.

Example

The following code creates a channel to buffer all USER_REQUEST actions. Note that even the Saga may be blocked
on the call effect. All actions that come while it's blocked are automatically buffered. This causes the Saga
to execute the API calls one at a time

import { actionChannel, call } from 'redux-saga/effects'
import api from '...'

function* takeOneAtMost() {
  const chan = yield actionChannel('USER_REQUEST')
  while (true) {
    const {payload} = yield take(chan)
    yield call(api.getUser, payload)
  }
}

flush(channel)

Creates an effect that instructs the middleware to flush all buffered items from the channel. Flushed items are returned back to the saga, so they can be utilized if needed.

Example

function* saga() {
  const chan = yield actionChannel('ACTION')

  try {
    while (true) {
      const action = yield take(chan)
      // ...
    }
  } finally {
    const actions = yield flush(chan)
    // ...
  }

}

cancelled()

Creates an effect that instructs the middleware to return whether this generator has been cancelled. Typically
you use this Effect in a finally block to run Cancellation specific code

Example

function* saga() {
  try {
    // ...
  } finally {
    if (yield cancelled()) {
      // logic that should execute only on Cancellation
    }
    // logic that should execute in all situations (e.g. closing a channel)
  }
}

setContext(props)

Creates an effect that instructs the middleware to update its own context. This effect extends
saga's context instead of replacing it.

getContext(prop)

Creates an effect that instructs the middleware to return a specific property of saga's context.

delay(ms, [val])

Returns an effect descriptor to block execution for ms milliseconds and return val value.

throttle(ms, pattern, saga, ...args)

Spawns a saga on an action dispatched to the Store that matches pattern. After spawning a task it's still accepting incoming actions into the underlying buffer, keeping at most 1 (the most recent one), but in the same time holding up with spawning new task for ms milliseconds (hence its name - throttle). Purpose of this is to ignore incoming actions for a given period of time while processing a task.

Example

In the following example, we create a basic task fetchAutocomplete. We use throttle to
start a new fetchAutocomplete task on dispatched FETCH_AUTOCOMPLETE action. However since throttle ignores consecutive FETCH_AUTOCOMPLETE for some time, we ensure that user won't flood our server with requests.

import { call, put, throttle } from `redux-saga/effects`

function* fetchAutocomplete(action) {
  const autocompleteProposals = yield call(Api.fetchAutocomplete, action.text)
  yield put({type: 'FETCHED_AUTOCOMPLETE_PROPOSALS', proposals: autocompleteProposals})
}

function* throttleAutocomplete() {
  yield throttle(1000, 'FETCH_AUTOCOMPLETE', fetchAutocomplete)
}

Notes

throttle is a high-level API built using take, fork and actionChannel. Here is how the helper could be implemented using the low-level Effects

const throttle = (ms, pattern, task, ...args) => fork(function*() {
  const throttleChannel = yield actionChannel(pattern, buffers.sliding(1))

  while (true) {
    const action = yield take(throttleChannel)
    yield fork(task, ...args, action)
    yield delay(ms)
  }
})

throttle(ms, channel, saga, ...args)

You can also handle a channel as argument and the behaviour is the same as throttle(ms, pattern, saga, ..args)

debounce(ms, pattern, saga, ...args)

Spawns a saga on an action dispatched to the Store that matches pattern. Saga will be called after it stops taking pattern actions for ms milliseconds. Purpose of this is to prevent calling saga until the actions are settled off.

Example

In the following example, we create a basic task fetchAutocomplete. We use debounce to
delay calling fetchAutocomplete saga until we stop receive any FETCH_AUTOCOMPLETE events for at least 1000 ms.

import { call, put, debounce } from `redux-saga/effects`

function* fetchAutocomplete(action) {
  const autocompleteProposals = yield call(Api.fetchAutocomplete, action.text)
  yield put({type: 'FETCHED_AUTOCOMPLETE_PROPOSALS', proposals: autocompleteProposals})
}

function* debounceAutocomplete() {
  yield debounce(1000, 'FETCH_AUTOCOMPLETE', fetchAutocomplete)
}

Notes

debounce is a high-level API built using take, delay and fork. Here is how the helper could be implemented using the low-level Effects

const debounce = (ms, pattern, task, ...args) => fork(function*() {
  while (true) {
    let action = yield take(pattern)

    while (true) {
      const { debounced, _action } = yield race({
        debounced: delay(ms),
        _action: take(pattern)
      })

      if (debounced) {
        yield fork(worker, ...args, action)
        break
      }

      action = _action
    }
  }
})

debounce(ms, channel, saga, ...args)

You can also handle a channel as argument and the behaviour is the same as debounce(ms, pattern, saga, ..args)

retry(maxTries, delay, fn, ...args)

Creates an Effect description that instructs the middleware to call the function fn with args as arguments.
In case of failure will try to make another call after delay milliseconds, if a number of attempts < maxTries.

Example

In the following example, we create a basic task retrySaga. We use retry to try to fetch our API 3 times with 10 second interval. If request fails first time than retry will call request one more time while calls count less than 3.

import { put, retry } from 'redux-saga/effects'
import { request } from 'some-api';

function* retrySaga(data) {
  try {
    const SECOND = 1000
    const response = yield retry(3, 10 * SECOND, request, data)
    yield put({ type: 'REQUEST_SUCCESS', payload: response })
  } catch(error) {
    yield put({ type: 'REQUEST_FAIL', payload: { error } })
  }
}

Notes

retry is a high-level API built using delay and call. Here is how the helper could be implemented using the low-level Effects

Effect combinators

race(effects)

Creates an Effect description that instructs the middleware to run a Race between
multiple Effects (this is similar to how Promise.race([...]) behaves).

effects: Object - a dictionary Object of the form {label: effect, …}

Example

The following example runs a race between two effects:

  1. A call to a function fetchUsers which returns a Promise
  2. A CANCEL_FETCH action which may be eventually dispatched on the Store
import { take, call, race } from `redux-saga/effects`
import fetchUsers from './path/to/fetchUsers'

function* fetchUsersSaga() {
  const { response, cancel } = yield race({
    response: call(fetchUsers),
    cancel: take(CANCEL_FETCH)
  })
}

If call(fetchUsers) resolves (or rejects) first, the result of race will be an object
with a single keyed object {response: result} where result is the resolved result of fetchUsers.

If an action of type CANCEL_FETCH is dispatched on the Store before fetchUsers completes, the result
will be a single keyed object {cancel: action}, where action is the dispatched action.

Notes

When resolving a race, the middleware automatically cancels all the losing Effects.

race([...effects]) (with Array)

The same as race(effects) but lets you pass in an array of effects.

Example

The following example runs a race between two effects:

  1. A call to a function fetchUsers which returns a Promise
  2. A CANCEL_FETCH action which may be eventually dispatched on the Store
import { take, call, race } from `redux-saga/effects`
import fetchUsers from './path/to/fetchUsers'

function* fetchUsersSaga() {
  const [response, cancel] = yield race([
    call(fetchUsers),
    take(CANCEL_FETCH)
  ])
}

If call(fetchUsers) resolves (or rejects) first, response will be an result of fetchUsers and cancel will be undefined.

If an action of type CANCEL_FETCH is dispatched on the Store before fetchUsers completes, response will be
undefined and cancel will be the dispatched action.

all([...effects]) - parallel effects

Creates an Effect description that instructs the middleware to run multiple Effects
in parallel and wait for all of them to complete. It's quite the corresponding API to standard Promise#all.

Example

The following example runs two blocking calls in parallel:

import { fetchCustomers, fetchProducts } from './path/to/api'
import { all, call } from `redux-saga/effects`

function* mySaga() {
  const [customers, products] = yield all([
    call(fetchCustomers),
    call(fetchProducts)
  ])
}

all(effects)

The same as all([...effects]) but lets you to pass in a dictionary object of effects with labels, just like race(effects)

Example

The following example runs two blocking calls in parallel:

import { fetchCustomers, fetchProducts } from './path/to/api'
import { all, call } from `redux-saga/effects`

function* mySaga() {
  const { customers, products } = yield all({
    customers: call(fetchCustomers),
    products: call(fetchProducts)
  })
}

Notes

When running Effects in parallel, the middleware suspends the Generator until one of the following occurs:

Interfaces

Task

The Task interface specifies the result of running a Saga using fork, middleware.run or runSaga.

method return value
task.isRunning() true if the task hasn't yet returned or thrown an error
task.isCancelled() true if the task has been cancelled
task.result() task return value. `undefined` if task is still running
task.error() task thrown error. `undefined` if task is still running
task.toPromise() a Promise which is either:
  • resolved with task's return value
  • rejected with task's thrown error
task.cancel() Cancels the task (If it is still running)

Channel

A channel is an object used to send and receive messages between tasks. Messages from senders are queued until an interested receiver request a message, and registered receiver is queued until a message is available.

Every channel has an underlying buffer which defines the buffering strategy (fixed size, dropping, sliding)

The Channel interface defines 3 methods: take, put and close

Channel.take(callback): used to register a taker. The take is resolved using the following rules

Channel.put(message): Used to put message on the buffer. The put will be handled using the following rules

Channel.flush(callback): Used to extract all buffered messages from the channel. The flush is resolved using the following rules

Channel.close(): closes the channel which means no more puts will be allowed. All pending takers will be invoked with END.

Buffer

Used to implement the buffering strategy for a channel. The Buffer interface defines 3 methods: isEmpty, put and take

SagaMonitor

Used by the middleware to dispatch monitoring events. Actually the middleware dispatches 6 events:

Below the signature for each method

## External API

runSaga(options, saga, ...args)

Allows starting sagas outside the Redux middleware environment. Useful if you want to
connect a Saga to external input/output, other than store actions.

runSaga returns a Task object. Just like the one returned from a fork effect.

Notes

The {channel, dispatch} is used to fulfill take and put Effects. This defines the Input/Output
interface of the Saga.

channel is used to fulfill take(PATTERN) effects. Every time something gets put on the channel it's
notifying all pending internal listeners. If the Saga is blocked on a take effect, and
if the take pattern matches the currently incoming input, the Saga is resumed with that input.

dispatch is used to fulfill put effects. Each time the Saga emits a yield put(output), dispatch
is invoked with output.

An example how to use this API may be found here.

Utils

channel([buffer])

A factory method that can be used to create Channels. You can optionally pass it a buffer
to control how the channel buffers the messages.

By default, if no buffer is provided, the channel will queue incoming messages up to 10 until interested takers are registered. The default buffering will deliver message using a FIFO strategy: a new taker will be delivered the oldest message in the buffer.

eventChannel(subscribe, [buffer])

Creates channel that will subscribe to an event source using the subscribe method. Incoming events from the event source will be queued in the channel until interested takers are registered.

To notify the channel that the event source has terminated, you can notify the provided subscriber with an END

Example

In the following example we create an event channel that will subscribe to a setInterval

const countdown = (secs) => {
  return eventChannel(emitter => {
      const iv = setInterval(() => {
        console.log('countdown', secs)
        secs -= 1
        if (secs > 0) {
          emitter(secs)
        } else {
          emitter(END)
          clearInterval(iv)
          console.log('countdown terminated')
        }
      }, 1000);
      return () => {
        clearInterval(iv)
        console.log('countdown cancelled')
      }
    }
  )
}

buffers

Provides some common buffers

cloneableGenerator(generatorFunc)

Takes a generator function (function*) and returns a generator function.
All generators instanciated from this function will be cloneable.
For testing purpose only.

Example

This is useful when you want to test a different branch of a saga without having to replay the actions that lead to it.

import { cloneableGenerator } from '@redux-saga/testing-utils';

function* oddOrEven() {
  // some stuff are done here
  yield 1;
  yield 2;
  yield 3;

  const userInput = yield 'enter a number';
  if (userInput % 2 === 0) {
    yield 'even';
  } else {
    yield 'odd'
  }
}

test('my oddOrEven saga', assert => {
  const data = {};
  data.gen = cloneableGenerator(oddOrEven)();

  assert.equal(
    data.gen.next().value,
    1,
    'it should yield 1'
  );

  assert.equal(
    data.gen.next().value,
    2,
    'it should yield 2'
  );

  assert.equal(
    data.gen.next().value,
    3,
    'it should yield 3'
  );

  assert.equal(
    data.gen.next().value,
    'enter a number',
    'it should ask for a number'
  );

  assert.test('even number is given', a => {
    // we make a clone of the generator before giving the number;
    data.clone = data.gen.clone();

    a.equal(
      data.gen.next(2).value,
      'even',
      'it should yield "even"'
    );

    a.equal(
      data.gen.next().done,
      true,
      'it should be done'
    );

    a.end();
  });

  assert.test('odd number is given', a => {

    a.equal(
      data.clone.next(1).value,
      'odd',
      'it should yield "odd"'
    );

    a.equal(
      data.clone.next().done,
      true,
      'it should be done'
    );

    a.end();
  });

  assert.end();
});

createMockTask()

Returns an object that mocks a task.
For testing purposes only.
See Task Cancellation docs for more information.
)

Cheatsheets

Blocking / Non-blocking

Name Blocking
takeEvery No
takeLatest No
takeLeading No
throttle No
debounce No
retry Yes
take Yes
take(channel) Sometimes (see API reference)
takeMaybe Yes
put No
putResolve Yes
put(channel, action) No
call Yes
apply Yes
cps Yes
fork No
spawn No
join Yes
cancel No
select No
actionChannel No
flush Yes
cancelled Yes
race Yes
delay Yes
all Blocks if there is a blocking effect in the array or object