1 | import { sym, is, ident, check, deprecate, updateIncentive, createSetContextWarning, SELF_CANCELLATION } from './utils'
|
2 | import { takeEveryHelper, takeLatestHelper, throttleHelper } from './sagaHelpers'
|
3 |
|
4 | const IO = sym('IO')
|
5 | const TAKE = 'TAKE'
|
6 | const PUT = 'PUT'
|
7 | const ALL = 'ALL'
|
8 | const RACE = 'RACE'
|
9 | const CALL = 'CALL'
|
10 | const CPS = 'CPS'
|
11 | const FORK = 'FORK'
|
12 | const JOIN = 'JOIN'
|
13 | const CANCEL = 'CANCEL'
|
14 | const SELECT = 'SELECT'
|
15 | const ACTION_CHANNEL = 'ACTION_CHANNEL'
|
16 | const CANCELLED = 'CANCELLED'
|
17 | const FLUSH = 'FLUSH'
|
18 | const GET_CONTEXT = 'GET_CONTEXT'
|
19 | const SET_CONTEXT = 'SET_CONTEXT'
|
20 |
|
21 | const TEST_HINT =
|
22 | '\n(HINT: if you are getting this errors in tests, consider using createMockTask from redux-saga/utils)'
|
23 |
|
24 | const effect = (type, payload) => ({ [IO]: true, [type]: payload })
|
25 |
|
26 | export function take(patternOrChannel = '*') {
|
27 | if (arguments.length) {
|
28 | check(arguments[0], is.notUndef, 'take(patternOrChannel): patternOrChannel is undefined')
|
29 | }
|
30 | if (is.pattern(patternOrChannel)) {
|
31 | return effect(TAKE, { pattern: patternOrChannel })
|
32 | }
|
33 | if (is.channel(patternOrChannel)) {
|
34 | return effect(TAKE, { channel: patternOrChannel })
|
35 | }
|
36 | throw new Error(
|
37 | `take(patternOrChannel): argument ${String(patternOrChannel)} is not valid channel or a valid pattern`,
|
38 | )
|
39 | }
|
40 |
|
41 | take.maybe = (...args) => {
|
42 | const eff = take(...args)
|
43 | eff[TAKE].maybe = true
|
44 | return eff
|
45 | }
|
46 |
|
47 | export const takem = deprecate(take.maybe, updateIncentive('takem', 'take.maybe'))
|
48 |
|
49 | export function put(channel, action) {
|
50 | if (arguments.length > 1) {
|
51 | check(channel, is.notUndef, 'put(channel, action): argument channel is undefined')
|
52 | check(channel, is.channel, `put(channel, action): argument ${channel} is not a valid channel`)
|
53 | check(action, is.notUndef, 'put(channel, action): argument action is undefined')
|
54 | } else {
|
55 | check(channel, is.notUndef, 'put(action): argument action is undefined')
|
56 | action = channel
|
57 | channel = null
|
58 | }
|
59 | return effect(PUT, { channel, action })
|
60 | }
|
61 |
|
62 | put.resolve = (...args) => {
|
63 | const eff = put(...args)
|
64 | eff[PUT].resolve = true
|
65 | return eff
|
66 | }
|
67 |
|
68 | put.sync = deprecate(put.resolve, updateIncentive('put.sync', 'put.resolve'))
|
69 |
|
70 | export function all(effects) {
|
71 | return effect(ALL, effects)
|
72 | }
|
73 |
|
74 | export function race(effects) {
|
75 | return effect(RACE, effects)
|
76 | }
|
77 |
|
78 | function getFnCallDesc(meth, fn, args) {
|
79 | check(fn, is.notUndef, `${meth}: argument fn is undefined`)
|
80 |
|
81 | let context = null
|
82 | if (is.array(fn)) {
|
83 | [context, fn] = fn
|
84 | } else if (fn.fn) {
|
85 | ({ context, fn } = fn)
|
86 | }
|
87 | if (context && is.string(fn) && is.func(context[fn])) {
|
88 | fn = context[fn]
|
89 | }
|
90 | check(fn, is.func, `${meth}: argument ${fn} is not a function`)
|
91 |
|
92 | return { context, fn, args }
|
93 | }
|
94 |
|
95 | export function call(fn, ...args) {
|
96 | return effect(CALL, getFnCallDesc('call', fn, args))
|
97 | }
|
98 |
|
99 | export function apply(context, fn, args = []) {
|
100 | return effect(CALL, getFnCallDesc('apply', { context, fn }, args))
|
101 | }
|
102 |
|
103 | export function cps(fn, ...args) {
|
104 | return effect(CPS, getFnCallDesc('cps', fn, args))
|
105 | }
|
106 |
|
107 | export function fork(fn, ...args) {
|
108 | return effect(FORK, getFnCallDesc('fork', fn, args))
|
109 | }
|
110 |
|
111 | export function spawn(fn, ...args) {
|
112 | const eff = fork(fn, ...args)
|
113 | eff[FORK].detached = true
|
114 | return eff
|
115 | }
|
116 |
|
117 | export function join(...tasks) {
|
118 | if (tasks.length > 1) {
|
119 | return all(tasks.map(t => join(t)))
|
120 | }
|
121 | const task = tasks[0]
|
122 | check(task, is.notUndef, 'join(task): argument task is undefined')
|
123 | check(task, is.task, `join(task): argument ${task} is not a valid Task object ${TEST_HINT}`)
|
124 | return effect(JOIN, task)
|
125 | }
|
126 |
|
127 | export function cancel(...tasks) {
|
128 | if (tasks.length > 1) {
|
129 | return all(tasks.map(t => cancel(t)))
|
130 | }
|
131 | const task = tasks[0]
|
132 | if (tasks.length === 1) {
|
133 | check(task, is.notUndef, 'cancel(task): argument task is undefined')
|
134 | check(task, is.task, `cancel(task): argument ${task} is not a valid Task object ${TEST_HINT}`)
|
135 | }
|
136 | return effect(CANCEL, task || SELF_CANCELLATION)
|
137 | }
|
138 |
|
139 | export function select(selector, ...args) {
|
140 | if (arguments.length === 0) {
|
141 | selector = ident
|
142 | } else {
|
143 | check(selector, is.notUndef, 'select(selector,[...]): argument selector is undefined')
|
144 | check(selector, is.func, `select(selector,[...]): argument ${selector} is not a function`)
|
145 | }
|
146 | return effect(SELECT, { selector, args })
|
147 | }
|
148 |
|
149 |
|
150 |
|
151 |
|
152 | export function actionChannel(pattern, buffer) {
|
153 | check(pattern, is.notUndef, 'actionChannel(pattern,...): argument pattern is undefined')
|
154 | if (arguments.length > 1) {
|
155 | check(buffer, is.notUndef, 'actionChannel(pattern, buffer): argument buffer is undefined')
|
156 | check(buffer, is.buffer, `actionChannel(pattern, buffer): argument ${buffer} is not a valid buffer`)
|
157 | }
|
158 | return effect(ACTION_CHANNEL, { pattern, buffer })
|
159 | }
|
160 |
|
161 | export function cancelled() {
|
162 | return effect(CANCELLED, {})
|
163 | }
|
164 |
|
165 | export function flush(channel) {
|
166 | check(channel, is.channel, `flush(channel): argument ${channel} is not valid channel`)
|
167 | return effect(FLUSH, channel)
|
168 | }
|
169 |
|
170 | export function getContext(prop) {
|
171 | check(prop, is.string, `getContext(prop): argument ${prop} is not a string`)
|
172 | return effect(GET_CONTEXT, prop)
|
173 | }
|
174 |
|
175 | export function setContext(props) {
|
176 | check(props, is.object, createSetContextWarning(null, props))
|
177 | return effect(SET_CONTEXT, props)
|
178 | }
|
179 |
|
180 | export function takeEvery(patternOrChannel, worker, ...args) {
|
181 | return fork(takeEveryHelper, patternOrChannel, worker, ...args)
|
182 | }
|
183 |
|
184 | export function takeLatest(patternOrChannel, worker, ...args) {
|
185 | return fork(takeLatestHelper, patternOrChannel, worker, ...args)
|
186 | }
|
187 |
|
188 | export function throttle(ms, pattern, worker, ...args) {
|
189 | return fork(throttleHelper, ms, pattern, worker, ...args)
|
190 | }
|
191 |
|
192 | const createAsEffectType = type => effect => effect && effect[IO] && effect[type]
|
193 |
|
194 | export const asEffect = {
|
195 | take: createAsEffectType(TAKE),
|
196 | put: createAsEffectType(PUT),
|
197 | all: createAsEffectType(ALL),
|
198 | race: createAsEffectType(RACE),
|
199 | call: createAsEffectType(CALL),
|
200 | cps: createAsEffectType(CPS),
|
201 | fork: createAsEffectType(FORK),
|
202 | join: createAsEffectType(JOIN),
|
203 | cancel: createAsEffectType(CANCEL),
|
204 | select: createAsEffectType(SELECT),
|
205 | actionChannel: createAsEffectType(ACTION_CHANNEL),
|
206 | cancelled: createAsEffectType(CANCELLED),
|
207 | flush: createAsEffectType(FLUSH),
|
208 | getContext: createAsEffectType(GET_CONTEXT),
|
209 | setContext: createAsEffectType(SET_CONTEXT),
|
210 | }
|