Saga middleware for Redux to handle side effects using ES6 generators
—
Effects for managing concurrent execution, forking tasks, and coordinating multiple asynchronous operations.
Creates an effect that performs a non-blocking call on a function. Returns a Task object representing the forked execution.
/**
* Perform non-blocking call, returns Task object
* @param fn - Function to fork (generator, async, or sync)
* @param args - Arguments to pass to the function
* @returns ForkEffect that resolves with Task
*/
function fork<Fn extends (...args: any[]) => any>(
fn: Fn,
...args: Parameters<Fn>
): ForkEffect<SagaReturnType<Fn>>;
/**
* Fork method on context object
* @param ctxAndFnName - Array of [context, methodName]
* @param args - Arguments to pass
* @returns ForkEffect
*/
function fork<Ctx extends { [P in Name]: (this: Ctx, ...args: any[]) => any }, Name extends string>(
ctxAndFnName: [Ctx, Name],
...args: Parameters<Ctx[Name]>
): ForkEffect<SagaReturnType<Ctx[Name]>>;Usage Examples:
import { fork, take, cancel } from "redux-saga/effects";
function* backgroundTask() {
while (true) {
console.log("Background work...");
yield delay(1000);
}
}
function* mainSaga() {
// Fork a background task
const task = yield fork(backgroundTask);
// Fork multiple tasks
const task1 = yield fork(fetchUser, 1);
const task2 = yield fork(fetchUser, 2);
// Parent waits for all forked tasks to complete
// before terminating (unless cancelled)
}Same as fork() but creates a detached task. A detached task remains independent from its parent and acts like a top-level task.
/**
* Create detached task independent from parent
* @param fn - Function to spawn
* @param args - Arguments to pass
* @returns ForkEffect with detached task
*/
function spawn<Fn extends (...args: any[]) => any>(
fn: Fn,
...args: Parameters<Fn>
): ForkEffect<SagaReturnType<Fn>>;Usage Examples:
import { spawn } from "redux-saga/effects";
function* parentSaga() {
// Spawn detached task
yield spawn(independentTask);
// Parent can complete without waiting for spawned task
// Spawned task continues running independently
}Creates an effect that waits for the result of a previously forked task. If the joined task is cancelled, the cancellation propagates to the saga executing the join effect.
/**
* Wait for result of previously forked task
* @param task - Task object from fork effect
* @returns JoinEffect that resolves with task result
*/
function join(task: Task): JoinEffect;
/**
* Wait for results of multiple tasks
* @param tasks - Array of Task objects
* @returns JoinEffect that resolves when all tasks complete
*/
function join(tasks: Task[]): JoinEffect;Usage Examples:
import { fork, join } from "redux-saga/effects";
function* coordinatorSaga() {
// Fork multiple tasks
const task1 = yield fork(fetchUser, 1);
const task2 = yield fork(fetchUser, 2);
// Wait for both to complete
const [user1, user2] = yield join([task1, task2]);
// Or join one at a time
const result1 = yield join(task1);
const result2 = yield join(task2);
}Creates an effect that cancels a previously forked task. Cancellation propagates to all child tasks and current effects.
/**
* Cancel a previously forked task
* @param task - Task object to cancel
* @returns CancelEffect
*/
function cancel(task: Task): CancelEffect;
/**
* Cancel multiple tasks
* @param tasks - Array of tasks to cancel
* @returns CancelEffect
*/
function cancel(tasks: Task[]): CancelEffect;
/**
* Self-cancellation (cancel current task)
* @returns CancelEffect for self-cancellation
*/
function cancel(): CancelEffect;Usage Examples:
import { fork, cancel, take, cancelled } from "redux-saga/effects";
function* cancellableTask() {
try {
while (true) {
yield call(doWork);
yield delay(1000);
}
} finally {
if (yield cancelled()) {
console.log('Task was cancelled');
// Cleanup logic
}
}
}
function* controllerSaga() {
const task = yield fork(cancellableTask);
// Cancel on user action
yield take('CANCEL_BACKGROUND_TASK');
yield cancel(task);
}Creates an effect that returns whether the current generator has been cancelled. Typically used in finally blocks for cleanup.
/**
* Check if current generator has been cancelled
* @returns CancelledEffect that resolves with boolean
*/
function cancelled(): CancelledEffect;Creates an effect that runs multiple effects in parallel and waits for all of them to complete. Similar to Promise.all().
/**
* Run multiple effects in parallel, wait for all to complete
* @param effects - Array of effects to run in parallel
* @returns AllEffect that resolves with array of results
*/
function all<T>(effects: T[]): AllEffect<T>;
/**
* Run labeled effects in parallel
* @param effects - Object with labeled effects
* @returns AllEffect that resolves with object of results
*/
function all<T>(effects: { [key: string]: T }): AllEffect<T>;Usage Examples:
import { all, call } from "redux-saga/effects";
function* fetchAllData() {
// Run multiple calls in parallel (array form)
const [users, posts, comments] = yield all([
call(fetchUsers),
call(fetchPosts),
call(fetchComments)
]);
// Run with labels (object form)
const { userData, profileData } = yield all({
userData: call(fetchUser, userId),
profileData: call(fetchProfile, userId)
});
}Creates an effect that runs a race between multiple effects. The first effect to complete wins, and all others are automatically cancelled.
/**
* Run race between multiple effects, first to complete wins
* @param effects - Object with labeled effects to race
* @returns RaceEffect that resolves with winner's result
*/
function race<T>(effects: { [key: string]: T }): RaceEffect<T>;
/**
* Race with array of effects
* @param effects - Array of effects to race
* @returns RaceEffect
*/
function race<T>(effects: T[]): RaceEffect<T>;Usage Examples:
import { race, call, take, delay } from "redux-saga/effects";
function* fetchWithTimeout() {
const { response, timeout } = yield race({
response: call(fetchUser, userId),
timeout: delay(5000, 'TIMEOUT')
});
if (timeout) {
console.log('Request timed out');
} else {
console.log('Got response:', response);
}
}
function* cancellableFetch() {
const { data, cancelled } = yield race({
data: call(fetchData),
cancelled: take('CANCEL_FETCH')
});
if (cancelled) {
console.log('Fetch was cancelled by user');
}
}Creates an effect that updates the saga's context. This extends the context rather than replacing it.
/**
* Update saga's context (extends existing context)
* @param props - Properties to add/update in context
* @returns SetContextEffect
*/
function setContext<C extends object>(props: C): SetContextEffect<C>;Creates an effect that returns a specific property of the saga's context.
/**
* Get property from saga's context
* @param prop - Property name to retrieve
* @returns GetContextEffect that resolves with property value
*/
function getContext(prop: string): GetContextEffect;Usage Examples:
import { setContext, getContext, call } from "redux-saga/effects";
function* apiSaga() {
// Set context
yield setContext({
apiClient: new ApiClient(),
userId: 123
});
// Get from context
const apiClient = yield getContext('apiClient');
const userId = yield getContext('userId');
const data = yield call([apiClient, 'fetchUser'], userId);
}Detaches a fork effect, making it run independently of its parent.
/**
* Detach a fork effect from its parent
* @param forkEffect - ForkEffect to detach
* @returns Detached ForkEffect
*/
function detach(forkEffect: ForkEffect): ForkEffect;Usage Examples:
import { fork, detach } from "redux-saga/effects";
function* parentSaga() {
// Create detached fork
yield detach(fork(backgroundService));
// Parent can complete without waiting for detached task
}Install with Tessl CLI
npx tessl i tessl/npm-redux-saga