API reference
Defaultsβ
defaultChannelβ
The defaultChannel
is the default EventEmitter used to emit Actions, which can then be awaited.
defaultTreeβ
The defaultTree
is the default Tree object used to maintain the parent-child relationships of Sagas.
Helpersβ
createActionβ
createAction(fn?, channel?)
creates an Action, which can be either empty or a wrapper for a function or model method. When this function is called, an action is dispatched to the defaultChannel. Such actions can be awaited with take, for instance.
Arguments:
fn?: FnActionInput
- Functionchannel?: EventEmitter
- Channel to dispatch an action, defaults to defaultChannel.
Returns: FnAction
- Function action.
Example:
const changeNewTodoText = createAction(); // create an empty action
const addTodo = createAction((text: string) => { // wrap an existing function
// ...
});
See examples or even Zustand tutorial for more information.
createAsyncActionβ
createAsyncAction(fn?, channel?)
creates an Action by wrapping an existing async function. It works the same as createAction, but await
s the async function's return result.
Arguments:
fn?: FnActionInput
- Async functionchannel?: EventEmitter
- Channel to dispatch an action, defaults to defaultChannel.
Returns: FnAction<Promise>
- Async function action.
Example:
const addTodo = createAsyncAction(async (text: string) => { // wrap an existing function
await Promise.resolve((resolve) => setTimeout(500, resolve));
// ...
});
createCreateSagaβ
createCreateSaga(config?)
creates a createSaga function with a bound plugin
and tree
.
Arguments:
config: SagaConfig:
plugin?: SagaPlugin
- Plugin with effects to inject into a wrapping async function. Merge plugins if you need more than one.tree?: SagaTreeNode
- Tree node object to maintain sagas' parent-child relationships.onError?: (err: unknown, node: SagaTreeNode) => void
-onError
handler. Refer to error handling for more information.
Returns: createSaga function.
Example:
import {createCreateSaga} from '@promise-saga/core';
import {plugin} from '@promise-saga/plugin-redux';
// bind an plugin
export const createSaga = createCreateSaga({plugin});
const saga = createSaga(async function() {
await this.delay(500);
// dispatch a Redux action
await this.dispatch({type: 'actions/test'});
});
Merge plugins, if you need more than one. See tutorials and examples for more information.
createHigherHooksβ
createHigherHooks(createSaga?, useSaga?)
creates a set of higher hooks.
Arguments:
createSaga: CreateSagaFunction
-createSaga
function. createCreateSaga creates its customized instance.useSaga: UseSagaHook
-useSaga
hook. createUseSaga creates its customized instance.
Returns: Set of higher hooks.
Example:
import {createCreateSaga} from '@promise-saga/core';
import {createUseSaga} from '@promise-saga/plugin-react';
import {plugin, createHigherHooks} from '@promise-saga/plugin-redux';
export const createSaga = createCreateSaga({plugin});
export const useSaga = createUseSaga({
runOnMount: false,
});
export const {
useTakeEvery,
useTakeLeading,
useTakeLatest,
useDebounce,
useThrottle,
} = createHigherHooks(createSaga, useSaga);
createSagaβ
createSaga(saga, config?)
creates a saga.
Arguments:
saga: SagaInput
- Async function to wrap and produce a Saga. This function will eventually get all plugins and effects inthis
.config: SagaConfig:
plugin?: SagaPlugin
- Plugin with effects to inject into the wrapping async function. Merge plugins if you need more than one.tree?: SagaTreeNode
- Tree node object to maintain sagas' parent-child relationships.onError?: (err: unknown, node: SagaTreeNode) => void
-onError
handler. Refer to error handling for more information.
Returns: Saga
Example:
const saga = createSaga(async function() {
await this.delay(500);
// ...
});
See tutorials and examples for more information.
createUseSagaβ
createUseSaga(config?)
creates a useSaga hook with preset configurations.
Arguments:
config: UseSagaConfig:
runOnMount?: boolean
- Whether to run Saga on React component mount, defaults totrue
.cancelOnUnmount?: boolean
- Whether to cancel Saga on React component unmount, defaults totrue
.
Returns: useSaga hook.
Example:
import {createUseSaga} from '@promise-saga/plugin-react';
import {plugin} from '@promise-saga/plugin-redux';
export const useSaga = createUseSaga({
runOnMount: false,
cancelOnUnmount: false,
});
isAbortErrorβ
Arguments:
err: unknown
- Presumed Error instance.
Returns: boolean
Helper to commonly refine errors from being handled. See the example in error handling.
import {isAbortError} from '@promise-saga/core';
export const saga = createSaga(async function () {
try {
await this.call(inner);
} catch (err) {
if (!isAbortError(err)) {
// handle error
}
}
});
mergePluginsβ
mergePlugins(...plugins)
merges multiple plugins into a single one.
Arguments:
...plugins: Plugin[]
- Plugins with effects to inject.
Returns: Plugin object.
Example:
import {createCreateSaga, mergePlugins} from '@promise-saga/core';
import reduxPlugin from '@promise-saga/plugin-redux';
import fetchPlugin from '@promise-saga/plugin-fetch';
const plugin = mergePlugins(reduxPlugin, fetchPlugin);
export const createSaga = createCreateSaga({plugin});
Effectsβ
allβ
await this.all(iter[])
- runs several tasks concurrently until all of them succeed.
Arguments:
iter[]: SagaIterator[]
- Concurrent SagaIterators.
Returns: SagaIterator<iterResult[]>
- await
s and returns a results array.
Example:
export const fetchAllPokemons = createSaga(async function() {
const [pikachu, raichu] = await this.all([
getPokemonData('pikachu'),
getPokemonData('raichu'),
]);
console.log({pikachu, raichu});
});
See concurrency for more information.
axiosβ
await this.get(url, options)
- Fetches response by URL. Works similarly to post
, put
, delete
, and others.
Arguments:
url: string
- URL to fetch.options: RequestConfig
- Axios RequestConfig.
Returns:: AxiosResponse
Example:
import {createCreateSaga} from '@promise-saga/core';
import {plugin} from '@promise-saga/plugin-axios';
const createSaga = createCreateSaga({plugin});
export const getPokemonData = createSaga(async function(pokemon: string) {
const resp = await this.get<PokemonData>(`https://pokeapi.co/api/v2/pokemon/${pokemon}`);
return resp.data;
});
callβ
await this.call(saga, ...args)
- Calls a Saga in a blocking way.
Arguments:
saga: Saga
- Saga to call.
Returns: SagaIterator.
See composing and non-blocking calls for more information.
Example:
const saga = createSaga(async function() {
return 2;
});
const main = createSaga(async function() {
// call saga in a blocking way
const result = await this.call(saga);
console.log(result); // 2
});
See composing and non blocking calls for more information.
callFnβ
this.callFn(sagaFn, ...args)
- Calls a function.
Arguments:
sagaFn: Function
- Function to be called.
Returns: Function return result.
Example:
const fn = () => 2;
const main = createSaga(async function() {
const result = this.callFn(fn); // 2
// ...
});
See composing for more information.
callPromiseβ
await this.callPromise(promise)
- Calls a promise.
Arguments:
promise: Promise
- Promise to be called.
Returns: Promise return result.
Example:
const promise = Promise.resolve(2);
const main = createSaga(async function() {
const result = await this.callPromise(promise); // 2
// ...
});
See composing for more information.
cancelβ
this.cancel(iter?)
- Cancels a non-blocking task or the current saga.
Arguments:
iter?: SagaIterator
- SagaIterator or simply a task to be cancelled.
Example:
const saga = createSaga(async function() {
// call child saga in a non-blocking way
const task = this.fork(childSaga);
// ...perform saga logic
this.cancel(task); // cancel task
});
See non blocking calls for more information.
cancellableβ
this.cancellable(promise, config?)
- Creates a cancellable promise, primarily used to create custom effects. For example:
Arguments:
promise: Promise
- Input promise.config: CancellableConfig:
onFinally?: () => void
- Function called on success, error, and cancellation. Can be used to teardown and unsubscribe from everything.onError?: (err: unknown) => void
- Function called on error.
Returns: SagaIterator.
Example: delay effect on github
export async function delay(this: SagaLowerEffectsScope, ms?: number) {
this.throwIfCancelled();
let timeout: NodeJS.Timeout;
// create a cancellable setTimeout
return this.cancellable<void>(new Promise((resolve) => {
timeout = setTimeout(() => {
this.throwIfCancelled();
resolve();
}, ms);
}), {
onFinally: () => clearTimeout(timeout), // finally clearTimeout
});
}
cancelledβ
this.cancelled(iter?)
- Checks if a non-blocking task or the current saga has already been cancelled.
Arguments:
iter?: SagaIterator
- SagaIterator or simply a task to be cancelled.
Returns: boolean
.
Example:
const parentSaga = createSaga(async function() {
// call child saga in a non-blocking way
const task = this.fork(childSaga);
// ...
await this.delay(1500); // perform saga logic
this.cancel(task); // cancel task after 1500ms
});
const childSaga = createSaga(async function() {
try {
await this.delay(3000); // perform saga logic for 3000ms
console.log('success!');
} finally {
console.log('finally', this.cancelled()); // true
}
});
See non blocking calls for more information.
debounceβ
this.debounce(ms, action, saga, ...args)
- Delays the execution of a Saga until the user stops performing an action for a specified amount of time.
Arguments:
ms: number
- Amount of time in milliseconds.action: Action
- Action to wait for.saga: Saga
- Saga to call.args: any[]
- Additional saga arguments to call with. The first saga incoming argument is the action that triggered it.
Returns: SagaIterator.
Example:
const saga = createSaga(async function() {
this.debounce(500, Todos.actions.toggleTodo, toggleTodo);
});
const toggleTodo = createSaga(async function() {
// ...
});
See debouncing and throttling for more information.
delayβ
await this.delay(ms?)
- Waits for a specified amount of time in milliseconds.
Arguments:
ms?: number
- Amount of time in milliseconds.
Example:
const saga = createSaga(async function() {
await this.delay(500);
console.log('done');
});
dispatchβ
this.dispatch(action)
- Dispatches a Redux action into a Store.
Arguments:
action: Action
- Redux action.
Example:
import {createAction} from '@reduxjs/toolkit';
export const addTodo = createAction<string>('todos/add');
export const startPokemonsRace = createSaga(async function() {
this.dispatch(addTodo('new todo'));
// ...
});
See redux tutorial for more information.
fetchβ
await this.fetch(url, options)
- Fetches a response by URL.
Arguments:
url: string
- URL to fetch.options: RequestInit
- Fetch request options.
Returns: Promise<T>
.
Example:
export const getPokemonData = createSaga(async function(pokemon: string) {
const resp = await this.fetch(`https://pokeapi.co/api/v2/pokemon/${pokemon}`);
const data: PokemonData = await resp.json();
return data;
});
forkβ
this.fork(saga, ...args)
- Forks a Saga in a detached non-blocking way.
Arguments:
saga: Saga
- Saga to fork.
Returns: SagaIterator.
Example:
const saga = createSaga(async function() {
// ...
});
const main = createSaga(async function() {
const task = this.fork(saga);
// returned task can be cancelled, when you need it
this.cancel(task);
});
See non blocking calls for more information.
getStoreβ
this.getStore()
- Returns a Redux Store inside a Saga. It may allow you some special usage of the Redux Store API.
Returns: Store.
Example:
import {Selector} from '@reduxjs/toolkit';
export const getTodos: Selector<RootState, Todo[]> = (state) => state.todos.todos;
const saga = createSaga(async function() {
// manual selector usage
const todos = getTodos(this.getStore().getState());
});
observableβ
this.observable(value)
- Observes a value
for its changes and provides a listener.
Arguments:
value: any
- Observing value.
Returns: Observable
.
Example:
const saga = createSaga(async function() {
const isOpen = this.observable(false); // create a boolean observable
setTimeout(() => {
isOpen.setValue(true); // set isOpen to true after 100ms
}, 100);
await this.race([
isOpen.onValue(true), // wait isOpen to become true
this.delay(500), // within timeout 500ms
]);
});
raceβ
await this.race(iter[])
- Runs several tasks concurrently until one of them succeeds.
Arguments:
iter[]: SagaIterator[]
- Concurrent SagaIterators.
Returns: SagaIterator<(iterResult | undefined)[]>
- await
s and returns a results array with multiple undefined
s and a single result filled in.
Example:
import {createAction} from '@reduxjs/toolkit';
export const cancelAction = createAction('action/cancel');
export const startPokemonsRace = createSaga(async function() {
const [pikachu, raichu] = await this.race([
getPokemonData('pikachu'),
getPokemonData('raichu'),
this.delay(500), // automatic cancel in 500 ms
this.take(cancelAction), // manual cancel on action dispatch
]);
console.log({pikachu, raichu});
});
See concurrency for more information.
selectβ
this.select(selector)
- Returns a Redux selector result.
Arguments:
selector: Selector
- Selector to be applied.
Returns: Selector result.
Example:
import {Selector} from '@reduxjs/toolkit';
export const getTodos: Selector<RootState, Todo[]> = (state) => state.todos.todos;
const saga = createSaga(async function() {
const todos = this.select(getTodos);
// ...
});
See redux tutorial for more information.
spawnβ
this.spawn(saga, ...args)
- Calls a Saga in a non-blocking detached way. Canceling the parent will not cause the cancellation of itself.
Arguments:
saga: Saga
- Saga to call.
Returns: SagaIterator.
Example:
const saga = createSaga(async function() {
const task = this.spawn(childSaga);
// ...perform saga logic
this.cancel(task);
});
See non blocking calls for more information.
takeβ
await this.take(action)
- Waits for a Redux/Zustand action to happen.
Arguments:
action: Action
- Redux action to wait for.
Returns: SagaIterator.
Example:
import {createAction} from '@reduxjs/toolkit';
const testAction = createAction('actions/test');
const saga = createSaga(async function() {
const action = await this.take(testAction);
console.log('done!', action); // { type: "actions/test" }
});
See future actions for more information.
takeEveryβ
this.takeEvery(action, saga, ...args)
- Binds a saga call to every action happening.
Arguments:
action: Action
- Action to wait for.saga: Saga
- Saga to call.args: any[]
- Additional saga arguments to call with. The first saga incoming argument is the action that triggered it.
Returns: SagaIterator.
Example:
// common watcher saga
const main = createSaga(async function() {
this.takeEvery(testAction1, saga1);
this.takeLatest(testAction2, saga2);
this.takeLeading(testAction3, saga3);
});
See future actions for more information.
takeLeadingβ
this.takeLeading(action, saga, ...args)
- Binds a saga call to the leading action happening.
Arguments:
action: Action
- Action to wait for.saga: Saga
- Saga to call.args: any[]
- Additional saga arguments to call with. The first saga incoming argument is the action that triggered it.
Returns: SagaIterator.
See takeEvery example and future actions for more information.
takeLatestβ
this.takeLatest(action, saga, ...args)
- Binds a saga call to the latest action happening.
Arguments:
action: Action
- Action to wait for.saga: Saga
- Saga to call.args: any[]
- Additional saga arguments to call with. The first saga incoming argument is the action that triggered it.
Returns: SagaIterator.
See takeEvery example and future actions for more information.
throttleβ
this.throttle(ms, action, saga, ...args)
- Limits the execution of a Saga to once in every specified time interval.
Arguments:
ms: number
- Time interval in milliseconds.action: Action
- Action to wait for.saga: Saga
- Saga to call.args: any[]
- Additional saga arguments to call with. The first saga incoming argument is the action that triggered it.
Returns: SagaIterator.
Example:
const saga = createSaga(async function() {
this.throttle(500, Todos.actions.addTodo, addTodo);
});
const addTodo = createSaga(async function() {
// ...
});
See debouncing and throttling for more information.
throwIfCancelledβ
this.throwIfCancelled()
- Throws an error with a reason, custom or default. This effect is similar to AbortSignal throwIfCancelled and is often used.
It can be used manually to cancel some heavy synchronous logic without effects in the process. For example:
const getHeavyResult = createSaga(async function() {
let i = 0;
while (i < 1e8) { // heavy task
i++;
if (i % 1e2 === 0) { // check if aborted each 100 iterations
this.throwIfCancelled();
}
}
return i;
});
Hooksβ
useDebounceβ
useDebounce(ms, action, saga, ...args)
- Wraps debounce into a hook.
Arguments:
ms: number
- Amount of time in milliseconds.action: Action
- Action to wait for.saga: Saga
- Saga to call.args: any[]
- Additional saga arguments to call with. The first saga incoming argument is the action that triggered it.
See debouncing and throttling for more information.
Example:
import React, {useState} from 'react';
import {createAction} from '@promise-saga/plugin-default';
import {createSaga, useDebounce} from '../zustandSaga';
// create an empty action to trigger manually on search text changed
const searchAction = createAction<void, [string]>();
export function SearchBlock() {
// create a React state
const [searchResults, setSearchResults] = useState<SearchResult[]>([]);
// debounce searchAction with 500ms delay and call a saga
useDebounce(500, searchAction, createSaga(async function({args: [pattern]}) {
// mutate a React state
setSearchResults(searchRegistry.search(pattern));
}));
// ...
}
See debouncing and throttling for more information.
useSagaβ
useSaga(saga, ...args)
- Calls a Saga, previously created with createSaga, with arguments within a React component. This way, the saga runs on component mount and is cancelled on unmount.
Example:
const SagaCheckbox = ({flow}: {flow: ReturnType<typeof useSaga>}) => (
<input type="checkbox" onChange={flow.toggle} checked={flow.isRunning} />
);
export default function App() {
const listenTogglesFlow = useSaga(listenTodoToggles);
return (
<label>
<SagaCheckbox flow={listenTogglesFlow} />
Log todos toggling, count by 3
</label>
);
}
useTakeEveryβ
useTakeEvery(action, saga, ...args)
- Wraps takeEvery into a hook.
Arguments:
action: Action
- Action to wait for.saga: Saga
- Saga to call.args: any[]
- Additional saga arguments to call with. The first saga incoming argument is the action that triggered it.
Returns: SagaIterator.
See future actions for more information.
Example:
import React, {useState} from 'react';
import {createAction} from '@promise-saga/plugin-default';
import {createSaga, useDebounce} from '../zustandSaga';
// create an empty action to trigger manually on search text changed
const searchAction = createAction<void, [string]>();
export function SearchBlock() {
// create a React state
const [searchResults, setSearchResults] = useState<SearchResult[]>([]);
// take every searchAction and call a saga
useTakeEvery(searchAction, createSaga(async function({args: [pattern]}) {
// mutate a React state
setSearchResults(searchRegistry.search(pattern));
}));
// ...
}
See future actions for more information.
useTakeLeadingβ
useTakeLeading(action, saga, ...args)
- Wraps takeLeading into a hook.
Arguments:
action: Action
- Action to wait for.saga: Saga
- Saga to call.args: any[]
- Additional saga arguments to call with. The first saga incoming argument is the action that triggered it.
Returns: SagaIterator.
See useTakeEvery example and future actions for more information.
useTakeLatestβ
useTakeLatest(action, saga, ...args)
- Wraps takeLatest into a hook.
Arguments:
action: Action
- Action to wait for.saga: Saga
- Saga to call.args: any[]
- Additional saga arguments to call with. The first saga incoming argument is the action that triggered it.
Returns: SagaIterator.
See useTakeEvery example and future actions for more information.
useThrottleβ
useThrottle(ms, action, saga, ...args)
- Wraps throttle into a hook.
Arguments:
ms: number
- Time interval in milliseconds.action: Action
- Action to wait for.saga: Saga
- Saga to call.args: any[]
- Additional saga arguments to call with. The first saga incoming argument is the action that triggered it.
See useDebounce example and future actions for more information.