Skip to content

API

Reference: https://redux-saga.js.org/docs/api

Middleware API

createSagaMiddleware(options)

See Operators.unsafeTake.

middleware.run(saga, ...args)

A Sonnet runs on instantiation, making this pattern obsolete.

Effect creators

take(pattern)

Original Reference 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:

  • If take is called with no arguments or '*' all dispatched actions are matched (e.g. take() will match all actions)

  • If it is a function, the action is matched if pattern(action) is true (e.g. take(action => action.entities) will match all actions having a (truthy) entities field.)

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.

  • If it is a String, the action is matched if action.type === pattern (e.g. take(INCREMENT_ASYNC))

  • If it is an array, each item in the array is matched with aforementioned rules, so the mixed array of strings and function predicates is supported. The most common use case is an array of strings though, so that action.type is matched against all items in the array (e.g. take([INCREMENT, DECREMENT]) and that would match either actions of type INCREMENT or DECREMENT).

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.

See Operators.unsafeTake.

takeMaybe(pattern)

See Operators.take.

take(channel)

See Operators.unsafeTakeFrom.

takeMaybe(channel)

See Operators.takeFrom.

takeEvery(pattern, saga, ...args)

See Operators.takeEvery.

takeEvery(channel, saga, ...args)

takeLatest(pattern, saga, ...args)

takeLatest(channel, saga, ...args)

takeLeading(pattern, saga, ...args)

takeLeading(channel, saga, ...args)

put(action)

putResolve(action)

put(channel, action)

call(fn, ...args)

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

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

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

apply(context, fn, [args])

cps(fn, ...args)

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

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

fork(fn, ...args)

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

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

spawn(fn, ...args)

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

join(task)

join([...tasks])

cancel(task)

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

  • task: Task - A Task object returned by a previous fork

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.

import {
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
,
import Fiber
Fiber
} from 'effect'
declare const
const myApi: Effect.Effect<void, never, never>
myApi
:
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
interface Effect<out A, out E = never, out R = never>

The Effect interface defines a value that describes a workflow or job, which can succeed or fail.

Details

The Effect interface represents a computation that can model a workflow involving various types of operations, such as synchronous, asynchronous, concurrent, and parallel interactions. It operates within a context of type R, and the result can either be a success with a value of type A or a failure with an error of type E. The Effect is designed to handle complex interactions with external resources, offering advanced features such as fiber-based concurrency, scheduling, interruption handling, and scalability. This makes it suitable for tasks that require fine-grained control over concurrency and error management.

To execute an Effect value, you need a Runtime, which provides the environment necessary to run and manage the computation.

@since2.0.0

@since2.0.0

Effect
<void, never, never>
const
const mySaga: Effect.Effect<void, never, never>
mySaga
=
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const gen: <YieldWrap<Effect.Effect<Fiber.RuntimeFiber<void, never>, never, never>> | YieldWrap<Effect.Effect<Exit<void, never>, never, never>>, void>(f: (resume: Effect.Adapter) => Generator<...>) => Effect.Effect<...> (+1 overload)

Provides a way to write effectful code using generator functions, simplifying control flow and error handling.

When to Use

Effect.gen allows you to write code that looks and behaves like synchronous code, but it can handle asynchronous tasks, errors, and complex control flow (like loops and conditions). It helps make asynchronous code more readable and easier to manage.

The generator functions work similarly to async/await but with more explicit control over the execution of effects. You can yield* values from effects and return the final result at the end.

@example

import { Effect } from "effect"
const addServiceCharge = (amount: number) => amount + 1
const applyDiscount = (
total: number,
discountRate: number
): Effect.Effect<number, Error> =>
discountRate === 0
? Effect.fail(new Error("Discount rate cannot be zero"))
: Effect.succeed(total - (total * discountRate) / 100)
const fetchTransactionAmount = Effect.promise(() => Promise.resolve(100))
const fetchDiscountRate = Effect.promise(() => Promise.resolve(5))
export const program = Effect.gen(function* () {
const transactionAmount = yield* fetchTransactionAmount
const discountRate = yield* fetchDiscountRate
const discountedAmount = yield* applyDiscount(
transactionAmount,
discountRate
)
const finalAmount = addServiceCharge(discountedAmount)
return `Final amount to charge: ${finalAmount}`
})

@since2.0.0

gen
(function*() {
const
const task: Fiber.RuntimeFiber<void, never>
task
= yield*
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const fork: <void, never, never>(self: Effect.Effect<void, never, never>) => Effect.Effect<Fiber.RuntimeFiber<void, never>, never, never>

Returns an effect that forks this effect into its own separate fiber, returning the fiber immediately, without waiting for it to begin executing the effect.

You can use the fork method whenever you want to execute an effect in a new fiber, concurrently and without "blocking" the fiber executing other effects. Using fibers can be tricky, so instead of using this method directly, consider other higher-level methods, such as raceWith, zipPar, and so forth.

The fiber returned by this method has methods to interrupt the fiber and to wait for it to finish executing the effect. See Fiber for more information.

Whenever you use this method to launch a new fiber, the new fiber is attached to the parent fiber's scope. This means when the parent fiber terminates, the child fiber will be terminated as well, ensuring that no fibers leak. This behavior is called "auto supervision", and if this behavior is not desired, you may use the forkDaemon or forkIn methods.

@since2.0.0

fork
(
const myApi: Effect.Effect<void, never, never>
myApi
)
yield*
import Fiber
Fiber
.
const interrupt: <void, never>(self: Fiber.Fiber<void, never>) => Effect.Effect<Exit<void, never>, never, never>

Interrupts the fiber from whichever fiber is calling this method. If the fiber has already exited, the returned effect will resume immediately. Otherwise, the effect will resume when the fiber exits.

@since2.0.0

interrupt
(
const task: Fiber.RuntimeFiber<void, never>
task
)
})

cancel([...tasks])

cancel()

select(selector, ...args)

Original Reference 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)).

  • selector: Function - a function (state, ...args) => args. It takes the current state and optionally some arguments and returns a slice of the current Store’s state

  • args: Array<any> - optional arguments to be passed to the selector in addition of getState.

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])

flush(channel)

cancelled()

setContext(props)

getContext(prop)

delay(ms, [val])

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

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

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

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

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

Effect combinators

race(effects)

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

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

all(effects)

Interfaces

Task

Channel

Buffer

SagaMonitor

External API

runSaga(options, saga, ...args)

Utils

channel([buffer])

eventChannel(subscribe, [buffer])

buffers

cloneableGenerator(generatorFunc)

createMockTask()

Cheatsheets

Blocking / Non-blocking