· 5 years ago · Sep 10, 2020, 11:50 AM
1import { Action, AnyAction } from 'redux'
2import { Last, Reverse } from 'typescript-tuple'
3
4import {
5 ActionPattern,
6 Effect,
7 Buffer,
8 CombinatorEffect,
9 CombinatorEffectDescriptor,
10 SimpleEffect,
11 END,
12 Pattern,
13 Predicate,
14 Task,
15 StrictEffect,
16 ActionMatchingPattern,
17 SagaIterator,
18} from '@redux-saga/types'
19
20import { FlushableChannel, PuttableChannel, TakeableChannel } from './index'
21
22export { ActionPattern, Effect, Pattern, SimpleEffect, StrictEffect }
23
24export const effectTypes: {
25 TAKE: 'TAKE'
26 PUT: 'PUT'
27 ALL: 'ALL'
28 RACE: 'RACE'
29 CALL: 'CALL'
30 CPS: 'CPS'
31 FORK: 'FORK'
32 JOIN: 'JOIN'
33 CANCEL: 'CANCEL'
34 SELECT: 'SELECT'
35 ACTION_CHANNEL: 'ACTION_CHANNEL'
36 CANCELLED: 'CANCELLED'
37 FLUSH: 'FLUSH'
38 GET_CONTEXT: 'GET_CONTEXT'
39 SET_CONTEXT: 'SET_CONTEXT'
40}
41
42/**
43 * Creates an Effect description that instructs the middleware to wait for a
44 * specified action on the Store. The Generator is suspended until an action
45 * that matches `pattern` is dispatched.
46 *
47 * The result of `yield take(pattern)` is an action object being dispatched.
48 *
49 * `pattern` is interpreted using the following rules:
50 *
51 * - If `take` is called with no arguments or `'*'` all dispatched actions are
52 * matched (e.g. `take()` will match all actions)
53 *
54 * - If it is a function, the action is matched if `pattern(action)` is true
55 * (e.g. `take(action => action.entities)` will match all actions having a
56 * (truthy) `entities` field.)
57 * > Note: if the pattern function has `toString` defined on it, `action.type`
58 * > will be tested against `pattern.toString()` instead. This is useful if
59 * > you're using an action creator library like redux-act or redux-actions.
60 *
61 * - If it is a String, the action is matched if `action.type === pattern` (e.g.
62 * `take(INCREMENT_ASYNC)`
63 *
64 * - If it is an array, each item in the array is matched with aforementioned
65 * rules, so the mixed array of strings and function predicates is supported.
66 * The most common use case is an array of strings though, so that
67 * `action.type` is matched against all items in the array (e.g.
68 * `take([INCREMENT, DECREMENT])` and that would match either actions of type
69 * `INCREMENT` or `DECREMENT`).
70 *
71 * The middleware provides a special action `END`. If you dispatch the END
72 * action, then all Sagas blocked on a take Effect will be terminated regardless
73 * of the specified pattern. If the terminated Saga has still some forked tasks
74 * which are still running, it will wait for all the child tasks to terminate
75 * before terminating the Task.
76 */
77export function take(pattern?: ActionPattern): TakeEffect
78export function take<A extends Action>(pattern?: ActionPattern<A>): TakeEffect
79
80/**
81 * Same as `take(pattern)` but does not automatically terminate the Saga on an
82 * `END` action. Instead all Sagas blocked on a take Effect will get the `END`
83 * object.
84 *
85 * #### Notes
86 *
87 * `takeMaybe` got its name from the FP analogy - it's like instead of having a
88 * return type of `ACTION` (with automatic handling) we can have a type of
89 * `Maybe(ACTION)` so we can handle both cases:
90 *
91 * - case when there is a `Just(ACTION)` (we have an action)
92 * - the case of `NOTHING` (channel was closed*). i.e. we need some way to map
93 * over `END`
94 *
95 * internally all `dispatch`ed actions are going through the `stdChannel` which
96 * is getting closed when `dispatch(END)` happens
97 */
98export function takeMaybe(pattern?: ActionPattern): TakeEffect
99export function takeMaybe<A extends Action>(pattern?: ActionPattern<A>): TakeEffect
100
101export type TakeEffect = SimpleEffect<'TAKE', TakeEffectDescriptor>
102
103export interface TakeEffectDescriptor {
104 pattern: ActionPattern
105 maybe?: boolean
106}
107
108/**
109 * Creates an Effect description that instructs the middleware to wait for a
110 * specified message from the provided Channel. If the channel is already
111 * closed, then the Generator will immediately terminate following the same
112 * process described above for `take(pattern)`.
113 */
114export function take<T>(channel: TakeableChannel<T>, multicastPattern?: Pattern<T>): ChannelTakeEffect<T>
115
116/**
117 * Same as `take(channel)` but does not automatically terminate the Saga on an
118 * `END` action. Instead all Sagas blocked on a take Effect will get the `END`
119 * object.
120 */
121export function takeMaybe<T>(channel: TakeableChannel<T>, multicastPattern?: Pattern<T>): ChannelTakeEffect<T>
122
123export type ChannelTakeEffect<T> = SimpleEffect<'TAKE', ChannelTakeEffectDescriptor<T>>
124
125export interface ChannelTakeEffectDescriptor<T> {
126 channel: TakeableChannel<T>
127 pattern?: Pattern<T>
128 maybe?: boolean
129}
130
131/**
132 * Spawns a `saga` on each action dispatched to the Store that matches
133 * `pattern`.
134 *
135 * #### Example
136 *
137 * In the following example, we create a basic task `fetchUser`. We use
138 * `takeEvery` to start a new `fetchUser` task on each dispatched
139 * `USER_REQUESTED` action:
140 *
141 * import { takeEvery } from `redux-saga/effects`
142 *
143 * function* fetchUser(action) {
144 * ...
145 * }
146 *
147 * function* watchFetchUser() {
148 * yield takeEvery('USER_REQUESTED', fetchUser)
149 * }
150 *
151 * #### Notes
152 *
153 * `takeEvery` is a high-level API built using `take` and `fork`. Here is how
154 * the helper could be implemented using the low-level Effects
155 *
156 * const takeEvery = (patternOrChannel, saga, ...args) => fork(function*() {
157 * while (true) {
158 * const action = yield take(patternOrChannel)
159 * yield fork(saga, ...args.concat(action))
160 * }
161 * })
162 *
163 * `takeEvery` allows concurrent actions to be handled. In the example above,
164 * when a `USER_REQUESTED` action is dispatched, a new `fetchUser` task is
165 * started even if a previous `fetchUser` is still pending (for example, the
166 * user clicks on a `Load User` button 2 consecutive times at a rapid rate, the
167 * 2nd click will dispatch a `USER_REQUESTED` action while the `fetchUser` fired
168 * on the first one hasn't yet terminated)
169 *
170 * `takeEvery` doesn't handle out of order responses from tasks. There is no
171 * guarantee that the tasks will terminate in the same order they were started.
172 * To handle out of order responses, you may consider `takeLatest` below.
173 *
174 * @param pattern for more information see docs for `take(pattern)`
175 * @param saga a Generator function
176 * @param args arguments to be passed to the started task. `takeEvery` will add
177 * the incoming action to the argument list (i.e. the action will be the last
178 * argument provided to `saga`)
179 */
180export function takeEvery<P extends ActionPattern>(
181 pattern: P,
182 worker: (action: ActionMatchingPattern<P>) => any,
183): ForkEffect<never>
184export function takeEvery<P extends ActionPattern, Fn extends (...args: any[]) => any>(
185 pattern: P,
186 worker: Fn,
187 ...args: HelperWorkerParameters<ActionMatchingPattern<P>, Fn>
188): ForkEffect<never>
189export function takeEvery<A extends Action>(
190 pattern: ActionPattern<A>,
191 worker: (action: A) => any,
192): ForkEffect<never>
193export function takeEvery<A extends Action, Fn extends (...args: any[]) => any>(
194 pattern: ActionPattern<A>,
195 worker: Fn,
196 ...args: HelperWorkerParameters<A, Fn>
197): ForkEffect<never>
198
199/**
200 * You can also pass in a channel as argument and the behaviour is the same as
201 * `takeEvery(pattern, saga, ...args)`.
202 */
203export function takeEvery<T>(
204 channel: TakeableChannel<T>,
205 worker: (item: T) => any,
206): ForkEffect<never>
207export function takeEvery<T, Fn extends (...args: any[]) => any>(
208 channel: TakeableChannel<T>,
209 worker: Fn,
210 ...args: HelperWorkerParameters<T, Fn>
211): ForkEffect<never>
212
213/**
214 * Spawns a `saga` on each action dispatched to the Store that matches
215 * `pattern`. And automatically cancels any previous `saga` task started
216 * previously if it's still running.
217 *
218 * Each time an action is dispatched to the store. And if this action matches
219 * `pattern`, `takeLatest` starts a new `saga` task in the background. If a
220 * `saga` task was started previously (on the last action dispatched before the
221 * actual action), and if this task is still running, the task will be
222 * cancelled.
223 *
224 * #### Example
225 *
226 * In the following example, we create a basic task `fetchUser`. We use
227 * `takeLatest` to start a new `fetchUser` task on each dispatched
228 * `USER_REQUESTED` action. Since `takeLatest` cancels any pending task started
229 * previously, we ensure that if a user triggers multiple consecutive
230 * `USER_REQUESTED` actions rapidly, we'll only conclude with the latest action
231 *
232 * import { takeLatest } from `redux-saga/effects`
233 *
234 * function* fetchUser(action) {
235 * ...
236 * }
237 *
238 * function* watchLastFetchUser() {
239 * yield takeLatest('USER_REQUESTED', fetchUser)
240 * }
241 *
242 * #### Notes
243 *
244 * `takeLatest` is a high-level API built using `take` and `fork`. Here is how
245 * the helper could be implemented using the low-level Effects
246 *
247 * const takeLatest = (patternOrChannel, saga, ...args) => fork(function*() {
248 * let lastTask
249 * while (true) {
250 * const action = yield take(patternOrChannel)
251 * if (lastTask) {
252 * yield cancel(lastTask) // cancel is no-op if the task has already terminated
253 * }
254 * lastTask = yield fork(saga, ...args.concat(action))
255 * }
256 * })
257 *
258 * @param pattern for more information see docs for [`take(pattern)`](#takepattern)
259 * @param saga a Generator function
260 * @param args arguments to be passed to the started task. `takeLatest` will add
261 * the incoming action to the argument list (i.e. the action will be the last
262 * argument provided to `saga`)
263 */
264export function takeLatest<P extends ActionPattern>(
265 pattern: P,
266 worker: (action: ActionMatchingPattern<P>) => any,
267): ForkEffect<never>
268export function takeLatest<P extends ActionPattern, Fn extends (...args: any[]) => any>(
269 pattern: P,
270 worker: Fn,
271 ...args: HelperWorkerParameters<ActionMatchingPattern<P>, Fn>
272): ForkEffect<never>
273export function takeLatest<A extends Action>(
274 pattern: ActionPattern<A>,
275 worker: (action: A) => any,
276): ForkEffect<never>
277export function takeLatest<A extends Action, Fn extends (...args: any[]) => any>(
278 pattern: ActionPattern<A>,
279 worker: Fn,
280 ...args: HelperWorkerParameters<A, Fn>
281): ForkEffect<never>
282
283/**
284 * You can also pass in a channel as argument and the behaviour is the same as
285 * `takeLatest(pattern, saga, ...args)`.
286 */
287export function takeLatest<T>(
288 channel: TakeableChannel<T>,
289 worker: (item: T) => any,
290): ForkEffect<never>
291export function takeLatest<T, Fn extends (...args: any[]) => any>(
292 channel: TakeableChannel<T>,
293 worker: Fn,
294 ...args: HelperWorkerParameters<T, Fn>
295): ForkEffect<never>
296
297/**
298 * Spawns a `saga` on each action dispatched to the Store that matches
299 * `pattern`. After spawning a task once, it blocks until spawned saga completes
300 * and then starts to listen for a `pattern` again.
301 *
302 * In short, `takeLeading` is listening for the actions when it doesn't run a
303 * saga.
304 *
305 * #### Example
306 *
307 * In the following example, we create a basic task `fetchUser`. We use
308 * `takeLeading` to start a new `fetchUser` task on each dispatched
309 * `USER_REQUESTED` action. Since `takeLeading` ignores any new coming task
310 * after it's started, we ensure that if a user triggers multiple consecutive
311 * `USER_REQUESTED` actions rapidly, we'll only keep on running with the leading
312 * action
313 *
314 * import { takeLeading } from `redux-saga/effects`
315 *
316 * function* fetchUser(action) {
317 * ...
318 * }
319 *
320 * function* watchLastFetchUser() {
321 * yield takeLeading('USER_REQUESTED', fetchUser)
322 * }
323 *
324 * #### Notes
325 *
326 * `takeLeading` is a high-level API built using `take` and `call`. Here is how
327 * the helper could be implemented using the low-level Effects
328 *
329 * const takeLeading = (patternOrChannel, saga, ...args) => fork(function*() {
330 * while (true) {
331 * const action = yield take(patternOrChannel);
332 * yield call(saga, ...args.concat(action));
333 * }
334 * })
335 *
336 * @param pattern for more information see docs for [`take(pattern)`](#takepattern)
337 * @param saga a Generator function
338 * @param args arguments to be passed to the started task. `takeLeading` will
339 * add the incoming action to the argument list (i.e. the action will be the
340 * last argument provided to `saga`)
341 */
342export function takeLeading<P extends ActionPattern>(
343 pattern: P,
344 worker: (action: ActionMatchingPattern<P>) => any,
345): ForkEffect<never>
346export function takeLeading<P extends ActionPattern, Fn extends (...args: any[]) => any>(
347 pattern: P,
348 worker: Fn,
349 ...args: HelperWorkerParameters<ActionMatchingPattern<P>, Fn>
350): ForkEffect<never>
351export function takeLeading<A extends Action>(
352 pattern: ActionPattern<A>,
353 worker: (action: A) => any,
354): ForkEffect<never>
355export function takeLeading<A extends Action, Fn extends (...args: any[]) => any>(
356 pattern: ActionPattern<A>,
357 worker: Fn,
358 ...args: HelperWorkerParameters<A, Fn>
359): ForkEffect<never>
360
361/**
362 * You can also pass in a channel as argument and the behaviour is the same as
363 * `takeLeading(pattern, saga, ...args)`.
364 */
365export function takeLeading<T>(
366 channel: TakeableChannel<T>,
367 worker: (item: T) => any,
368): ForkEffect<never>
369export function takeLeading<T, Fn extends (...args: any[]) => any>(
370 channel: TakeableChannel<T>,
371 worker: Fn,
372 ...args: HelperWorkerParameters<T, Fn>
373): ForkEffect<never>
374
375export type HelperWorkerParameters<T, Fn extends (...args: any[]) => any> = Last<Parameters<Fn>> extends T
376 ? AllButLast<Parameters<Fn>>
377 : Parameters<Fn>
378
379/**
380 * Creates an Effect description that instructs the middleware to dispatch an
381 * action to the Store. This effect is non-blocking and any errors that are
382 * thrown downstream (e.g. in a reducer) will not bubble back into the saga.
383 *
384 * @param action [see Redux `dispatch` documentation for complete info](http://redux.js.org/docs/api/Store.html#dispatch)
385 */
386export function put<A extends Action>(action: A): PutEffect<A>
387
388/**
389 * Just like `put` but the effect is blocking (if promise is returned from
390 * `dispatch` it will wait for its resolution) and will bubble up errors from
391 * downstream.
392 *
393 * @param action [see Redux `dispatch` documentation for complete info](http://redux.js.org/docs/api/Store.html#dispatch)
394 */
395export function putResolve<A extends Action>(action: A): PutEffect<A>
396
397export type PutEffect<A extends Action = AnyAction> = SimpleEffect<'PUT', PutEffectDescriptor<A>>
398
399export interface PutEffectDescriptor<A extends Action> {
400 action: A
401 channel: null
402 resolve?: boolean
403}
404
405/**
406 * Creates an Effect description that instructs the middleware to put an action
407 * into the provided channel.
408 *
409 * This effect is blocking if the put is *not* buffered but immediately consumed
410 * by takers. If an error is thrown in any of these takers it will bubble back
411 * into the saga.
412 *
413 * @param channel a `Channel` Object.
414 * @param action [see Redux `dispatch` documentation for complete info](http://redux.js.org/docs/api/Store.html#dispatch)
415 */
416export function put<T>(channel: PuttableChannel<T>, action: T | END): ChannelPutEffect<T>
417
418export type ChannelPutEffect<T> = SimpleEffect<'PUT', ChannelPutEffectDescriptor<T>>
419
420export interface ChannelPutEffectDescriptor<T> {
421 action: T
422 channel: PuttableChannel<T>
423}
424
425/**
426 * Creates an Effect description that instructs the middleware to call the
427 * function `fn` with `args` as arguments.
428 *
429 * #### Notes
430 *
431 * `fn` can be either a *normal* or a Generator function.
432 *
433 * The middleware invokes the function and examines its result.
434 *
435 * If the result is an Iterator object, the middleware will run that Generator
436 * function, just like it did with the startup Generators (passed to the
437 * middleware on startup). The parent Generator will be suspended until the
438 * child Generator terminates normally, in which case the parent Generator is
439 * resumed with the value returned by the child Generator. Or until the child
440 * aborts with some error, in which case an error will be thrown inside the
441 * parent Generator.
442 *
443 * If `fn` is a normal function and returns a Promise, the middleware will
444 * suspend the Generator until the Promise is settled. After the promise is
445 * resolved the Generator is resumed with the resolved value, or if the Promise
446 * is rejected an error is thrown inside the Generator.
447 *
448 * If the result is not an Iterator object nor a Promise, the middleware will
449 * immediately return that value back to the saga, so that it can resume its
450 * execution synchronously.
451 *
452 * When an error is thrown inside the Generator, if it has a `try/catch` block
453 * surrounding the current `yield` instruction, the control will be passed to
454 * the `catch` block. Otherwise, the Generator aborts with the raised error, and
455 * if this Generator was called by another Generator, the error will propagate
456 * to the calling Generator.
457 *
458 * @param fn A Generator function, or normal function which either returns a
459 * Promise as result, or any other value.
460 * @param args An array of values to be passed as arguments to `fn`
461 */
462export function call<Fn extends (...args: any[]) => any>(
463 fn: Fn,
464 ...args: Parameters<Fn>,
465): CallEffect<SagaReturnType<Fn>>
466/**
467 * Same as `call([context, fn], ...args)` but supports passing a `fn` as string.
468 * Useful for invoking object's methods, i.e.
469 * `yield call([localStorage, 'getItem'], 'redux-saga')`
470 */
471export function call<Ctx extends { [P in Name]: (this: Ctx, ...args: any[]) => any }, Name extends string>(
472 ctxAndFnName: [Ctx, Name],
473 ...args: Parameters<Ctx[Name]>
474): CallEffect<SagaReturnType<Ctx[Name]>>
475/**
476 * Same as `call([context, fn], ...args)` but supports passing `context` and
477 * `fn` as properties of an object, i.e.
478 * `yield call({context: localStorage, fn: localStorage.getItem}, 'redux-saga')`.
479 * `fn` can be a string or a function.
480 */
481export function call<Ctx extends { [P in Name]: (this: Ctx, ...args: any[]) => any }, Name extends string>(
482 ctxAndFnName: { context: Ctx; fn: Name },
483 ...args: Parameters<Ctx[Name]>
484): CallEffect<SagaReturnType<Ctx[Name]>>
485/**
486 * Same as `call(fn, ...args)` but supports passing a `this` context to `fn`.
487 * This is useful to invoke object methods.
488 */
489export function call<Ctx, Fn extends (this: Ctx, ...args: any[]) => any>(
490 ctxAndFn: [Ctx, Fn],
491 ...args: Parameters<Fn>
492): CallEffect<SagaReturnType<Fn>>
493/**
494 * Same as `call([context, fn], ...args)` but supports passing `context` and
495 * `fn` as properties of an object, i.e.
496 * `yield call({context: localStorage, fn: localStorage.getItem}, 'redux-saga')`.
497 * `fn` can be a string or a function.
498 */
499export function call<Ctx, Fn extends (this: Ctx, ...args: any[]) => any>(
500 ctxAndFn: { context: Ctx; fn: Fn },
501 ...args: Parameters<Fn>
502): CallEffect<SagaReturnType<Fn>>
503
504export type CallEffect<RT = any> = SimpleEffect<'CALL', CallEffectDescriptor<RT>>
505
506export interface CallEffectDescriptor<RT> {
507 context: any
508 fn: ((...args: any[]) => SagaIterator<RT> | Promise<RT> | RT)
509 args: any[]
510}
511
512export type SagaReturnType<S extends Function> =
513 S extends (...args: any[]) => SagaIterator<infer RT> ? RT :
514 S extends (...args: any[]) => Promise<infer RT> ? RT :
515 S extends (...args: any[]) => infer RT ? RT :
516 never;
517
518/**
519 * Alias for `call([context, fn], ...args)`.
520 */
521export function apply<Ctx extends { [P in Name]: (this: Ctx, ...args: any[]) => any }, Name extends string>(
522 ctx: Ctx,
523 fnName: Name,
524 args: Parameters<Ctx[Name]>,
525): CallEffect<SagaReturnType<Ctx[Name]>>
526export function apply<Ctx, Fn extends (this: Ctx, ...args: any[]) => any>(
527 ctx: Ctx,
528 fn: Fn,
529 args: Parameters<Fn>,
530): CallEffect<SagaReturnType<Fn>>
531
532/**
533 * Creates an Effect description that instructs the middleware to invoke `fn` as
534 * a Node style function.
535 *
536 * @param fn a Node style function. i.e. a function which accepts in addition to
537 * its arguments, an additional callback to be invoked by `fn` when it
538 * terminates. The callback accepts two parameters, where the first parameter
539 * is used to report errors while the second is used to report successful
540 * results
541 * @param args an array to be passed as arguments for `fn`
542 */
543export function cps<Fn extends (cb: CpsCallback<any>) => any>(fn: Fn): CpsEffect<ReturnType<Fn>>
544export function cps<Fn extends (...args: any[]) => any>(
545 fn: Fn,
546 ...args: CpsFunctionParameters<Fn>
547): CpsEffect<ReturnType<Fn>>
548/**
549 * Same as `cps([context, fn], ...args)` but supports passing a `fn` as string.
550 * Useful for invoking object's methods, i.e.
551 * `yield cps([localStorage, 'getItem'], 'redux-saga')`
552 */
553export function cps<Ctx extends { [P in Name]: (this: Ctx, ...args: any[]) => void }, Name extends string>(
554 ctxAndFnName: [Ctx, Name],
555 ...args: CpsFunctionParameters<Ctx[Name]>
556): CpsEffect<ReturnType<Ctx[Name]>>
557/**
558 * Same as `cps([context, fn], ...args)` but supports passing `context` and
559 * `fn` as properties of an object, i.e.
560 * `yield cps({context: localStorage, fn: localStorage.getItem}, 'redux-saga')`.
561 * `fn` can be a string or a function.
562 */
563export function cps<Ctx extends { [P in Name]: (this: Ctx, ...args: any[]) => void }, Name extends string>(
564 ctxAndFnName: { context: Ctx; fn: Name },
565 ...args: CpsFunctionParameters<Ctx[Name]>
566): CpsEffect<ReturnType<Ctx[Name]>>
567/**
568 * Same as `cps(fn, ...args)` but supports passing a `this` context to `fn`.
569 * This is useful to invoke object methods.
570 */
571export function cps<Ctx, Fn extends (this: Ctx, ...args: any[]) => void>(
572 ctxAndFn: [Ctx, Fn],
573 ...args: CpsFunctionParameters<Fn>
574): CpsEffect<ReturnType<Fn>>
575/**
576 * Same as `cps([context, fn], ...args)` but supports passing `context` and
577 * `fn` as properties of an object, i.e.
578 * `yield cps({context: localStorage, fn: localStorage.getItem}, 'redux-saga')`.
579 * `fn` can be a string or a function.
580 */
581export function cps<Ctx, Fn extends (this: Ctx, ...args: any[]) => void>(
582 ctxAndFn: { context: Ctx; fn: Fn },
583 ...args: CpsFunctionParameters<Fn>
584): CpsEffect<ReturnType<Fn>>
585
586export type CpsFunctionParameters<Fn extends (...args: any[]) => any> = Last<Parameters<Fn>> extends CpsCallback<any>
587 ? AllButLast<Parameters<Fn>>
588 : never
589
590export interface CpsCallback<R> {
591 (error: any, result: R): void
592 cancel?(): void
593}
594
595export type CpsEffect<RT> = SimpleEffect<'CPS', CallEffectDescriptor<RT>>
596
597/**
598 * Creates an Effect description that instructs the middleware to perform a
599 * *non-blocking call* on `fn`
600 *
601 * returns a `Task` object.
602 *
603 * #### Note
604 *
605 * `fork`, like `call`, can be used to invoke both normal and Generator
606 * functions. But, the calls are non-blocking, the middleware doesn't suspend
607 * the Generator while waiting for the result of `fn`. Instead as soon as `fn`
608 * is invoked, the Generator resumes immediately.
609 *
610 * `fork`, alongside `race`, is a central Effect for managing concurrency
611 * between Sagas.
612 *
613 * The result of `yield fork(fn ...args)` is a `Task` object. An object
614 * with some useful methods and properties.
615 *
616 * All forked tasks are *attached* to their parents. When the parent terminates
617 * the execution of its own body of instructions, it will wait for all forked
618 * tasks to terminate before returning.
619 *
620 * Errors from child tasks automatically bubble up to their parents. If any
621 * forked task raises an uncaught error, then the parent task will abort with
622 * the child Error, and the whole Parent's execution tree (i.e. forked tasks +
623 * the *main task* represented by the parent's body if it's still running) will
624 * be cancelled.
625 *
626 * Cancellation of a forked Task will automatically cancel all forked tasks that
627 * are still executing. It'll also cancel the current Effect where the cancelled
628 * task was blocked (if any).
629 *
630 * If a forked task fails *synchronously* (ie: fails immediately after its
631 * execution before performing any async operation), then no Task is returned,
632 * instead the parent will be aborted as soon as possible (since both parent and
633 * child execute in parallel, the parent will abort as soon as it takes notice
634 * of the child failure).
635 *
636 * To create *detached* forks, use `spawn` instead.
637 *
638 * @param fn A Generator function, or normal function which returns a Promise as result
639 * @param args An array of values to be passed as arguments to `fn`
640 */
641export function fork<Fn extends (...args: any[]) => any>(
642 fn: Fn,
643 ...args: Parameters<Fn>,
644): ForkEffect<SagaReturnType<Fn>>
645/**
646 * Same as `fork([context, fn], ...args)` but supports passing a `fn` as string.
647 * Useful for invoking object's methods, i.e.
648 * `yield fork([localStorage, 'getItem'], 'redux-saga')`
649 */
650export function fork<Ctx extends { [P in Name]: (this: Ctx, ...args: any[]) => any }, Name extends string>(
651 ctxAndFnName: [Ctx, Name],
652 ...args: Parameters<Ctx[Name]>
653): ForkEffect<SagaReturnType<Ctx[Name]>>
654/**
655 * Same as `fork([context, fn], ...args)` but supports passing `context` and
656 * `fn` as properties of an object, i.e.
657 * `yield fork({context: localStorage, fn: localStorage.getItem}, 'redux-saga')`.
658 * `fn` can be a string or a function.
659 */
660export function fork<Ctx extends { [P in Name]: (this: Ctx, ...args: any[]) => any }, Name extends string>(
661 ctxAndFnName: { context: Ctx; fn: Name },
662 ...args: Parameters<Ctx[Name]>
663): ForkEffect<SagaReturnType<Ctx[Name]>>
664/**
665 * Same as `fork(fn, ...args)` but supports passing a `this` context to `fn`.
666 * This is useful to invoke object methods.
667 */
668export function fork<Ctx, Fn extends (this: Ctx, ...args: any[]) => any>(
669 ctxAndFn: [Ctx, Fn],
670 ...args: Parameters<Fn>
671): ForkEffect<SagaReturnType<Fn>>
672/**
673 * Same as `fork([context, fn], ...args)` but supports passing `context` and
674 * `fn` as properties of an object, i.e.
675 * `yield fork({context: localStorage, fn: localStorage.getItem}, 'redux-saga')`.
676 * `fn` can be a string or a function.
677 */
678export function fork<Ctx, Fn extends (this: Ctx, ...args: any[]) => any>(
679 ctxAndFn: { context: Ctx; fn: Fn },
680 ...args: Parameters<Fn>
681): ForkEffect<SagaReturnType<Fn>>
682
683export type ForkEffect<RT = any> = SimpleEffect<'FORK', ForkEffectDescriptor<RT>>
684
685export interface ForkEffectDescriptor<RT> extends CallEffectDescriptor<RT> {
686 detached?: boolean
687}
688
689/**
690 * Same as `fork(fn, ...args)` but creates a *detached* task. A detached task
691 * remains independent from its parent and acts like a top-level task. The
692 * parent will not wait for detached tasks to terminate before returning and all
693 * events which may affect the parent or the detached task are completely
694 * independents (error, cancellation).
695 */
696export function spawn<Fn extends (...args: any[]) => any>(
697 fn: Fn,
698 ...args: Parameters<Fn>
699): ForkEffect<SagaReturnType<Fn>>
700/**
701 * Same as `spawn([context, fn], ...args)` but supports passing a `fn` as string.
702 * Useful for invoking object's methods, i.e.
703 * `yield spawn([localStorage, 'getItem'], 'redux-saga')`
704 */
705export function spawn<Ctx extends { [P in Name]: (this: Ctx, ...args: any[]) => any }, Name extends string>(
706 ctxAndFnName: [Ctx, Name],
707 ...args: Parameters<Ctx[Name]>
708): ForkEffect<SagaReturnType<Ctx[Name]>>
709/**
710 * Same as `spawn([context, fn], ...args)` but supports passing `context` and
711 * `fn` as properties of an object, i.e.
712 * `yield spawn({context: localStorage, fn: localStorage.getItem}, 'redux-saga')`.
713 * `fn` can be a string or a function.
714 */
715export function spawn<Ctx extends { [P in Name]: (this: Ctx, ...args: any[]) => any }, Name extends string>(
716 ctxAndFnName: { context: Ctx; fn: Name },
717 ...args: Parameters<Ctx[Name]>
718): ForkEffect<SagaReturnType<Ctx[Name]>>
719/**
720 * Same as `spawn(fn, ...args)` but supports passing a `this` context to `fn`.
721 * This is useful to invoke object methods.
722 */
723export function spawn<Ctx, Fn extends (this: Ctx, ...args: any[]) => any>(
724 ctxAndFn: [Ctx, Fn],
725 ...args: Parameters<Fn>
726): ForkEffect<SagaReturnType<Fn>>
727/**
728 * Same as `spawn([context, fn], ...args)` but supports passing `context` and
729 * `fn` as properties of an object, i.e.
730 * `yield spawn({context: localStorage, fn: localStorage.getItem}, 'redux-saga')`.
731 * `fn` can be a string or a function.
732 */
733export function spawn<Ctx, Fn extends (this: Ctx, ...args: any[]) => any>(
734 ctxAndFn: { context: Ctx; fn: Fn },
735 ...args: Parameters<Fn>
736): ForkEffect<SagaReturnType<Fn>>
737
738/**
739 * Creates an Effect description that instructs the middleware to wait for the
740 * result of a previously forked task.
741 *
742 * #### Notes
743 *
744 * `join` will resolve to the same outcome of the joined task (success or
745 * error). If the joined task is cancelled, the cancellation will also propagate
746 * to the Saga executing the join effect. Similarly, any potential callers of
747 * those joiners will be cancelled as well.
748 *
749 * @param task A `Task` object returned by a previous `fork`
750 */
751export function join(task: Task): JoinEffect
752/**
753 * Creates an Effect description that instructs the middleware to wait for the
754 * results of previously forked tasks.
755 *
756 * @param tasks A `Task` is the object returned by a previous `fork`
757 */
758export function join(tasks: Task[]): JoinEffect
759
760export type JoinEffect = SimpleEffect<'JOIN', JoinEffectDescriptor>
761
762export type JoinEffectDescriptor = Task | Task[]
763
764/**
765 * Creates an Effect description that instructs the middleware to cancel a
766 * previously forked task.
767 *
768 * #### Notes
769 *
770 * To cancel a running task, the middleware will invoke `return` on the
771 * underlying Generator object. This will cancel the current Effect in the task
772 * and jump to the finally block (if defined).
773 *
774 * Inside the finally block, you can execute any cleanup logic or dispatch some
775 * action to keep the store in a consistent state (e.g. reset the state of a
776 * spinner to false when an ajax request is cancelled). You can check inside the
777 * finally block if a Saga was cancelled by issuing a `yield cancelled()`.
778 *
779 * Cancellation propagates downward to child sagas. When cancelling a task, the
780 * middleware will also cancel the current Effect (where the task is currently
781 * blocked). If the current Effect is a call to another Saga, it will be also
782 * cancelled. When cancelling a Saga, all *attached forks* (sagas forked using
783 * `yield fork()`) will be cancelled. This means that cancellation effectively
784 * affects the whole execution tree that belongs to the cancelled task.
785 *
786 * `cancel` is a non-blocking Effect. i.e. the Saga executing it will resume
787 * immediately after performing the cancellation.
788 *
789 * For functions which return Promise results, you can plug your own
790 * cancellation logic by attaching a `[CANCEL]` to the promise.
791 *
792 * The following example shows how to attach cancellation logic to a Promise
793 * result:
794 *
795 * import { CANCEL } from 'redux-saga'
796 * import { fork, cancel } from 'redux-saga/effects'
797 *
798 * function myApi() {
799 * const promise = myXhr(...)
800 *
801 * promise[CANCEL] = () => myXhr.abort()
802 * return promise
803 * }
804 *
805 * function* mySaga() {
806 *
807 * const task = yield fork(myApi)
808 *
809 * // ... later
810 * // will call promise[CANCEL] on the result of myApi
811 * yield cancel(task)
812 * }
813 *
814 * redux-saga will automatically cancel jqXHR objects using their `abort` method.
815 *
816 * @param task A `Task` object returned by a previous `fork`
817 */
818export function cancel(task: Task): CancelEffect
819/**
820 * Creates an Effect description that instructs the middleware to cancel
821 * previously forked tasks.
822 *
823 * #### Notes
824 *
825 * It wraps the array of tasks in cancel effects, roughly becoming the
826 * equivalent of `yield tasks.map(t => cancel(t))`.
827 *
828 * @param tasks A `Task` is the object returned by a previous `fork`
829 */
830export function cancel(tasks: Task[]): CancelEffect
831/**
832 * Creates an Effect description that instructs the middleware to cancel a task
833 * in which it has been yielded (self-cancellation). It allows to reuse
834 * destructor-like logic inside a `finally` blocks for both outer
835 * (`cancel(task)`) and self (`cancel()`) cancellations.
836 *
837 * #### Example
838 *
839 * function* deleteRecord({ payload }) {
840 * try {
841 * const { confirm, deny } = yield call(prompt);
842 * if (confirm) {
843 * yield put(actions.deleteRecord.confirmed())
844 * }
845 * if (deny) {
846 * yield cancel()
847 * }
848 * } catch(e) {
849 * // handle failure
850 * } finally {
851 * if (yield cancelled()) {
852 * // shared cancellation logic
853 * yield put(actions.deleteRecord.cancel(payload))
854 * }
855 * }
856 * }
857 */
858export function cancel(): CancelEffect
859
860export type CancelEffect = SimpleEffect<'CANCEL', CancelEffectDescriptor>
861
862export type CancelEffectDescriptor = Task | Task[] | SELF_CANCELLATION
863type SELF_CANCELLATION = '@@redux-saga/SELF_CANCELLATION'
864
865/**
866 * Creates an effect that instructs the middleware to invoke the provided
867 * selector on the current Store's state (i.e. returns the result of
868 * `selector(getState(), ...args)`).
869 *
870 * If `select` is called without argument (i.e. `yield select()`) then the
871 * effect is resolved with the entire state (the same result of a `getState()`
872 * call).
873 *
874 * > It's important to note that when an action is dispatched to the store, the
875 * middleware first forwards the action to the reducers and then notifies the
876 * Sagas. This means that when you query the Store's State, you get the State
877 * **after** the action has been applied. However, this behavior is only
878 * guaranteed if all subsequent middlewares call `next(action)` synchronously.
879 * If any subsequent middleware calls `next(action)` asynchronously (which is
880 * unusual but possible), then the sagas will get the state from **before** the
881 * action is applied. Therefore it is recommended to review the source of each
882 * subsequent middleware to ensure it calls `next(action)` synchronously, or
883 * else ensure that redux-saga is the last middleware in the call chain.
884 *
885 * #### Notes
886 *
887 * Preferably, a Saga should be autonomous and should not depend on the Store's
888 * state. This makes it easy to modify the state implementation without
889 * affecting the Saga code. A saga should preferably depend only on its own
890 * internal control state when possible. But sometimes, one could find it more
891 * convenient for a Saga to query the state instead of maintaining the needed
892 * data by itself (for example, when a Saga duplicates the logic of invoking
893 * some reducer to compute a state that was already computed by the Store).
894 *
895 * For example, suppose we have this state shape in our application:
896 *
897 * state = {
898 * cart: {...}
899 * }
900 *
901 * We can create a *selector*, i.e. a function which knows how to extract the
902 * `cart` data from the State:
903 *
904 * `./selectors`
905 *
906 * export const getCart = state => state.cart
907 *
908 *
909 * Then we can use that selector from inside a Saga using the `select` Effect:
910 *
911 * `./sagas.js`
912 *
913 * import { take, fork, select } from 'redux-saga/effects'
914 * import { getCart } from './selectors'
915 *
916 * function* checkout() {
917 * // query the state using the exported selector
918 * const cart = yield select(getCart)
919 *
920 * // ... call some API endpoint then dispatch a success/error action
921 * }
922 *
923 * export default function* rootSaga() {
924 * while (true) {
925 * yield take('CHECKOUT_REQUEST')
926 * yield fork(checkout)
927 * }
928 * }
929 *
930 * `checkout` can get the needed information directly by using
931 * `select(getCart)`. The Saga is coupled only with the `getCart` selector. If
932 * we have many Sagas (or React Components) that needs to access the `cart`
933 * slice, they will all be coupled to the same function `getCart`. And if we now
934 * change the state shape, we need only to update `getCart`.
935 *
936 * @param selector a function `(state, ...args) => args`. It takes the current
937 * state and optionally some arguments and returns a slice of the current
938 * Store's state
939 * @param args optional arguments to be passed to the selector in addition of
940 * `getState`.
941 */
942export function select(): SelectEffect
943export function select<Fn extends (state: any, ...args: any[]) => any>(
944 selector: Fn,
945 ...args: Tail<Parameters<Fn>>
946): SelectEffect
947
948export type SelectEffect = SimpleEffect<'SELECT', SelectEffectDescriptor>
949
950export interface SelectEffectDescriptor {
951 selector(state: any, ...args: any[]): any
952 args: any[]
953}
954
955/**
956 * Creates an effect that instructs the middleware to queue the actions matching
957 * `pattern` using an event channel. Optionally, you can provide a buffer to
958 * control buffering of the queued actions.
959 *
960 * #### Example
961 *
962 * The following code creates a channel to buffer all `USER_REQUEST` actions.
963 * Note that even the Saga may be blocked on the `call` effect. All actions that
964 * come while it's blocked are automatically buffered. This causes the Saga to
965 * execute the API calls one at a time
966 *
967 * import { actionChannel, call } from 'redux-saga/effects'
968 * import api from '...'
969 *
970 * function* takeOneAtMost() {
971 * const chan = yield actionChannel('USER_REQUEST')
972 * while (true) {
973 * const {payload} = yield take(chan)
974 * yield call(api.getUser, payload)
975 * }
976 * }
977 *
978 * @param pattern see API for `take(pattern)`
979 * @param buffer a `Buffer` object
980 */
981export function actionChannel(pattern: ActionPattern, buffer?: Buffer<Action>): ActionChannelEffect
982
983export type ActionChannelEffect = SimpleEffect<'ACTION_CHANNEL', ActionChannelEffectDescriptor>
984
985export interface ActionChannelEffectDescriptor {
986 pattern: ActionPattern
987 buffer?: Buffer<Action>
988}
989
990/**
991 * Creates an effect that instructs the middleware to flush all buffered items
992 * from the channel. Flushed items are returned back to the saga, so they can be
993 * utilized if needed.
994 *
995 * #### Example
996 *
997 * function* saga() {
998 * const chan = yield actionChannel('ACTION')
999 *
1000 * try {
1001 * while (true) {
1002 * const action = yield take(chan)
1003 * // ...
1004 * }
1005 * } finally {
1006 * const actions = yield flush(chan)
1007 * // ...
1008 * }
1009 * }
1010 *
1011 * @param channel a `Channel` Object.
1012 */
1013export function flush<T>(channel: FlushableChannel<T>): FlushEffect<T>
1014
1015export type FlushEffect<T> = SimpleEffect<'FLUSH', FlushEffectDescriptor<T>>
1016
1017export type FlushEffectDescriptor<T> = FlushableChannel<T>
1018
1019/**
1020 * Creates an effect that instructs the middleware to return whether this
1021 * generator has been cancelled. Typically you use this Effect in a finally
1022 * block to run Cancellation specific code
1023 *
1024 * #### Example
1025 *
1026 * function* saga() {
1027 * try {
1028 * // ...
1029 * } finally {
1030 * if (yield cancelled()) {
1031 * // logic that should execute only on Cancellation
1032 * }
1033 * // logic that should execute in all situations (e.g. closing a channel)
1034 * }
1035 * }
1036 */
1037export function cancelled(): CancelledEffect
1038
1039export type CancelledEffect = SimpleEffect<'CANCELLED', CancelledEffectDescriptor>
1040
1041export type CancelledEffectDescriptor = {}
1042
1043/**
1044 * Creates an effect that instructs the middleware to update its own context.
1045 * This effect extends saga's context instead of replacing it.
1046 */
1047export function setContext<C extends object>(props: C): SetContextEffect<C>
1048
1049export type SetContextEffect<C extends object> = SimpleEffect<'SET_CONTEXT', SetContextEffectDescriptor<C>>
1050
1051export type SetContextEffectDescriptor<C extends object> = C
1052
1053/**
1054 * Creates an effect that instructs the middleware to return a specific property
1055 * of saga's context.
1056 */
1057export function getContext(prop: string): GetContextEffect
1058
1059export type GetContextEffect = SimpleEffect<'GET_CONTEXT', GetContextEffectDescriptor>
1060
1061export type GetContextEffectDescriptor = string
1062
1063/**
1064 * Returns an effect descriptor to block execution for `ms` milliseconds and return `val` value.
1065 */
1066export function delay<T = true>(ms: number, val?: T): CallEffect<T>
1067
1068/**
1069 * Spawns a `saga` on an action dispatched to the Store that matches `pattern`.
1070 * After spawning a task it's still accepting incoming actions into the
1071 * underlying `buffer`, keeping at most 1 (the most recent one), but in the same
1072 * time holding up with spawning new task for `ms` milliseconds (hence its name -
1073 * `throttle`). Purpose of this is to ignore incoming actions for a given
1074 * period of time while processing a task.
1075 *
1076 * #### Example
1077 *
1078 * In the following example, we create a basic task `fetchAutocomplete`. We use
1079 * `throttle` to start a new `fetchAutocomplete` task on dispatched
1080 * `FETCH_AUTOCOMPLETE` action. However since `throttle` ignores consecutive
1081 * `FETCH_AUTOCOMPLETE` for some time, we ensure that user won't flood our
1082 * server with requests.
1083 *
1084 * import { call, put, throttle } from `redux-saga/effects`
1085 *
1086 * function* fetchAutocomplete(action) {
1087 * const autocompleteProposals = yield call(Api.fetchAutocomplete, action.text)
1088 * yield put({type: 'FETCHED_AUTOCOMPLETE_PROPOSALS', proposals: autocompleteProposals})
1089 * }
1090 *
1091 * function* throttleAutocomplete() {
1092 * yield throttle(1000, 'FETCH_AUTOCOMPLETE', fetchAutocomplete)
1093 * }
1094 *
1095 * #### Notes
1096 *
1097 * `throttle` is a high-level API built using `take`, `fork` and
1098 * `actionChannel`. Here is how the helper could be implemented using the
1099 * low-level Effects
1100 *
1101 * const throttle = (ms, pattern, task, ...args) => fork(function*() {
1102 * const throttleChannel = yield actionChannel(pattern, buffers.sliding(1))
1103 *
1104 * while (true) {
1105 * const action = yield take(throttleChannel)
1106 * yield fork(task, ...args, action)
1107 * yield delay(ms)
1108 * }
1109 * })
1110 *
1111 * @param ms length of a time window in milliseconds during which actions will
1112 * be ignored after the action starts processing
1113 * @param pattern for more information see docs for `take(pattern)`
1114 * @param saga a Generator function
1115 * @param args arguments to be passed to the started task. `throttle` will add
1116 * the incoming action to the argument list (i.e. the action will be the last
1117 * argument provided to `saga`)
1118 */
1119export function throttle<P extends ActionPattern>(
1120 ms: number,
1121 pattern: P,
1122 worker: (action: ActionMatchingPattern<P>) => any,
1123): ForkEffect<never>
1124export function throttle<P extends ActionPattern, Fn extends (...args: any[]) => any>(
1125 ms: number,
1126 pattern: P,
1127 worker: Fn,
1128 ...args: HelperWorkerParameters<ActionMatchingPattern<P>, Fn>
1129): ForkEffect<never>
1130export function throttle<A extends Action>(
1131 ms: number,
1132 pattern: ActionPattern<A>,
1133 worker: (action: A) => any,
1134): ForkEffect<never>
1135export function throttle<A extends Action, Fn extends (...args: any[]) => any>(
1136 ms: number,
1137 pattern: ActionPattern<A>,
1138 worker: Fn,
1139 ...args: HelperWorkerParameters<A, Fn>
1140): ForkEffect<never>
1141
1142/**
1143 * You can also pass in a channel as argument and the behaviour is the same as
1144 * `throttle(ms, pattern, saga, ...args)`.
1145 */
1146export function throttle<T>(
1147 ms: number,
1148 channel: TakeableChannel<T>,
1149 worker: (item: T) => any,
1150): ForkEffect<never>
1151export function throttle<T, Fn extends (...args: any[]) => any>(
1152 ms: number,
1153 channel: TakeableChannel<T>,
1154 worker: Fn,
1155 ...args: HelperWorkerParameters<T, Fn>
1156): ForkEffect<never>
1157
1158/**
1159 * Spawns a `saga` on an action dispatched to the Store that matches `pattern`.
1160 * Saga will be called after it stops taking `pattern` actions for `ms`
1161 * milliseconds. Purpose of this is to prevent calling saga until the actions
1162 * are settled off.
1163 *
1164 * #### Example
1165 *
1166 * In the following example, we create a basic task `fetchAutocomplete`. We use
1167 * `debounce` to delay calling `fetchAutocomplete` saga until we stop receive
1168 * any `FETCH_AUTOCOMPLETE` events for at least `1000` ms.
1169 *
1170 * import { call, put, debounce } from `redux-saga/effects`
1171 *
1172 * function* fetchAutocomplete(action) {
1173 * const autocompleteProposals = yield call(Api.fetchAutocomplete, action.text)
1174 * yield put({type: 'FETCHED_AUTOCOMPLETE_PROPOSALS', proposals: autocompleteProposals})
1175 * }
1176 *
1177 * function* debounceAutocomplete() {
1178 * yield debounce(1000, 'FETCH_AUTOCOMPLETE', fetchAutocomplete)
1179 * }
1180 *
1181 * #### Notes
1182 *
1183 * `debounce` is a high-level API built using `take`, `delay` and `fork`. Here
1184 * is how the helper could be implemented using the low-level Effects
1185 *
1186 * const debounce = (ms, pattern, task, ...args) => fork(function*() {
1187 * while (true) {
1188 * let action = yield take(pattern)
1189 *
1190 * while (true) {
1191 * const { debounced, _action } = yield race({
1192 * debounced: delay(ms),
1193 * _action: take(pattern)
1194 * })
1195 *
1196 * if (debounced) {
1197 * yield fork(worker, ...args, action)
1198 * break
1199 * }
1200 *
1201 * action = _action
1202 * }
1203 * }
1204 * })
1205 *
1206 * @param ms defines how many milliseconds should elapse since the last time
1207 * `pattern` action was fired to call the `saga`
1208 * @param pattern for more information see docs for `take(pattern)`
1209 * @param saga a Generator function
1210 * @param args arguments to be passed to the started task. `debounce` will add
1211 * the incoming action to the argument list (i.e. the action will be the last
1212 * argument provided to `saga`)
1213 */
1214export function debounce<P extends ActionPattern>(
1215 ms: number,
1216 pattern: P,
1217 worker: (action: ActionMatchingPattern<P>) => any,
1218): ForkEffect<never>
1219export function debounce<P extends ActionPattern, Fn extends (...args: any[]) => any>(
1220 ms: number,
1221 pattern: P,
1222 worker: Fn,
1223 ...args: HelperWorkerParameters<ActionMatchingPattern<P>, Fn>
1224): ForkEffect<never>
1225export function debounce<A extends Action>(
1226 ms: number,
1227 pattern: ActionPattern<A>,
1228 worker: (action: A) => any,
1229): ForkEffect<never>
1230export function debounce<A extends Action, Fn extends (...args: any[]) => any>(
1231 ms: number,
1232 pattern: ActionPattern<A>,
1233 worker: Fn,
1234 ...args: HelperWorkerParameters<A, Fn>
1235): ForkEffect<never>
1236
1237/**
1238 * You can also pass in a channel as argument and the behaviour is the same as
1239 * `debounce(ms, pattern, saga, ...args)`.
1240 */
1241export function debounce<T>(
1242 ms: number,
1243 channel: TakeableChannel<T>,
1244 worker: (item: T) => any,
1245): ForkEffect<never>
1246export function debounce<T, Fn extends (...args: any[]) => any>(
1247 ms: number,
1248 channel: TakeableChannel<T>,
1249 worker: Fn,
1250 ...args: HelperWorkerParameters<T, Fn>
1251): ForkEffect<never>
1252
1253/**
1254 * Creates an Effect description that instructs the middleware to call the
1255 * function `fn` with `args` as arguments. In case of failure will try to make
1256 * another call after `delay` milliseconds, if a number of attempts < `maxTries`.
1257 *
1258 * #### Example
1259 *
1260 * In the following example, we create a basic task `retrySaga`. We use `retry`
1261 * to try to fetch our API 3 times with 10 second interval. If `request` fails
1262 * first time than `retry` will call `request` one more time while calls count
1263 * less than 3.
1264 *
1265 * import { put, retry } from 'redux-saga/effects'
1266 * import { request } from 'some-api';
1267 *
1268 * function* retrySaga(data) {
1269 * try {
1270 * const SECOND = 1000
1271 * const response = yield retry(3, 10 * SECOND, request, data)
1272 * yield put({ type: 'REQUEST_SUCCESS', payload: response })
1273 * } catch(error) {
1274 * yield put({ type: 'REQUEST_FAIL', payload: { error } })
1275 * }
1276 * }
1277 *
1278 * @param maxTries maximum calls count.
1279 * @param delay length of a time window in milliseconds between `fn` calls.
1280 * @param fn A Generator function, or normal function which either returns a
1281 * Promise as a result, or any other value.
1282 * @param args An array of values to be passed as arguments to `fn`
1283 */
1284export function retry<Fn extends (...args: any[]) => any>(
1285 maxTries: number,
1286 delayLength: number,
1287 fn: Fn,
1288 ...args: Parameters<Fn>
1289): CallEffect<SagaReturnType<Fn>>
1290
1291/**
1292 * Creates an Effect description that instructs the middleware to run multiple
1293 * Effects in parallel and wait for all of them to complete. It's quite the
1294 * corresponding API to standard
1295 * [`Promise#all`](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Promise/all).
1296 *
1297 * #### Example
1298 *
1299 * The following example runs two blocking calls in parallel:
1300 *
1301 * import { fetchCustomers, fetchProducts } from './path/to/api'
1302 * import { all, call } from `redux-saga/effects`
1303 *
1304 * function* mySaga() {
1305 * const [customers, products] = yield all([
1306 * call(fetchCustomers),
1307 * call(fetchProducts)
1308 * ])
1309 * }
1310 */
1311export function all<T>(effects: T[]): AllEffect<T>
1312/**
1313 * The same as `all([...effects])` but let's you to pass in a dictionary object
1314 * of effects with labels, just like `race(effects)`
1315 *
1316 * @param effects a dictionary Object of the form {label: effect, ...}
1317 */
1318export function all<T>(effects: { [key: string]: T }): AllEffect<T>
1319
1320export type AllEffect<T> = CombinatorEffect<'ALL', T>
1321
1322export type AllEffectDescriptor<T> = CombinatorEffectDescriptor<T>
1323
1324/**
1325 * Creates an Effect description that instructs the middleware to run a *Race*
1326 * between multiple Effects (this is similar to how
1327 * [`Promise.race([...])`](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Promise/race)
1328 * behaves).
1329 *
1330 *
1331 * #### Example
1332 *
1333 * The following example runs a race between two effects:
1334 *
1335 * 1. A call to a function `fetchUsers` which returns a Promise
1336 * 2. A `CANCEL_FETCH` action which may be eventually dispatched on the Store
1337 *
1338 *
1339 * import { take, call, race } from `redux-saga/effects`
1340 * import fetchUsers from './path/to/fetchUsers'
1341 *
1342 * function* fetchUsersSaga() {
1343 * const { response, cancel } = yield race({
1344 * response: call(fetchUsers),
1345 * cancel: take(CANCEL_FETCH)
1346 * })
1347 * }
1348 *
1349 * If `call(fetchUsers)` resolves (or rejects) first, the result of `race` will
1350 * be an object with a single keyed object `{response: result}` where `result`
1351 * is the resolved result of `fetchUsers`.
1352 *
1353 * If an action of type `CANCEL_FETCH` is dispatched on the Store before
1354 * `fetchUsers` completes, the result will be a single keyed object
1355 * `{cancel: action}`, where action is the dispatched action.
1356 *
1357 * #### Notes
1358 *
1359 * When resolving a `race`, the middleware automatically cancels all the losing
1360 * Effects.
1361 *
1362 * @param effects a dictionary Object of the form {label: effect, ...}
1363 */
1364export function race<T>(effects: { [key: string]: T }): RaceEffect<T>
1365/**
1366 * The same as `race(effects)` but lets you pass in an array of effects.
1367 */
1368export function race<T>(effects: T[]): RaceEffect<T>
1369
1370export type RaceEffect<T> = CombinatorEffect<'RACE', T>
1371
1372export type RaceEffectDescriptor<T> = CombinatorEffectDescriptor<T>
1373
1374/**
1375 * [H, ...T] -> T
1376 */
1377export type Tail<L extends any[]> = ((...l: L) => any) extends ((h: any, ...t: infer T) => any) ? T : never
1378/**
1379 * [...A, B] -> A
1380 */
1381export type AllButLast<L extends any[]> = Reverse<Tail<Reverse<L>>>
1382