UNPKG

6.38 kBJavaScriptView Raw
1import { sym, is, ident, check, deprecate, updateIncentive, createSetContextWarning, SELF_CANCELLATION } from './utils'
2import { takeEveryHelper, takeLatestHelper, throttleHelper } from './sagaHelpers'
3
4const IO = sym('IO')
5const TAKE = 'TAKE'
6const PUT = 'PUT'
7const ALL = 'ALL'
8const RACE = 'RACE'
9const CALL = 'CALL'
10const CPS = 'CPS'
11const FORK = 'FORK'
12const JOIN = 'JOIN'
13const CANCEL = 'CANCEL'
14const SELECT = 'SELECT'
15const ACTION_CHANNEL = 'ACTION_CHANNEL'
16const CANCELLED = 'CANCELLED'
17const FLUSH = 'FLUSH'
18const GET_CONTEXT = 'GET_CONTEXT'
19const SET_CONTEXT = 'SET_CONTEXT'
20
21const TEST_HINT =
22 '\n(HINT: if you are getting this errors in tests, consider using createMockTask from redux-saga/utils)'
23
24const effect = (type, payload) => ({ [IO]: true, [type]: payload })
25
26export 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
41take.maybe = (...args) => {
42 const eff = take(...args)
43 eff[TAKE].maybe = true
44 return eff
45}
46
47export const takem = /*#__PURE__*/ deprecate(take.maybe, /*#__PURE__*/ updateIncentive('takem', 'take.maybe'))
48
49export 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
62put.resolve = (...args) => {
63 const eff = put(...args)
64 eff[PUT].resolve = true
65 return eff
66}
67
68put.sync = deprecate(put.resolve, updateIncentive('put.sync', 'put.resolve'))
69
70export function all(effects) {
71 return effect(ALL, effects)
72}
73
74export function race(effects) {
75 return effect(RACE, effects)
76}
77
78function 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
95export function call(fn, ...args) {
96 return effect(CALL, getFnCallDesc('call', fn, args))
97}
98
99export function apply(context, fn, args = []) {
100 return effect(CALL, getFnCallDesc('apply', { context, fn }, args))
101}
102
103export function cps(fn, ...args) {
104 return effect(CPS, getFnCallDesc('cps', fn, args))
105}
106
107export function fork(fn, ...args) {
108 return effect(FORK, getFnCallDesc('fork', fn, args))
109}
110
111export function spawn(fn, ...args) {
112 const eff = fork(fn, ...args)
113 eff[FORK].detached = true
114 return eff
115}
116
117export 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
127export 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
139export 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 channel(pattern, [buffer]) => creates an event channel for store actions
151**/
152export 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
161export function cancelled() {
162 return effect(CANCELLED, {})
163}
164
165export function flush(channel) {
166 check(channel, is.channel, `flush(channel): argument ${channel} is not valid channel`)
167 return effect(FLUSH, channel)
168}
169
170export function getContext(prop) {
171 check(prop, is.string, `getContext(prop): argument ${prop} is not a string`)
172 return effect(GET_CONTEXT, prop)
173}
174
175export function setContext(props) {
176 check(props, is.object, createSetContextWarning(null, props))
177 return effect(SET_CONTEXT, props)
178}
179
180export function takeEvery(patternOrChannel, worker, ...args) {
181 return fork(takeEveryHelper, patternOrChannel, worker, ...args)
182}
183
184export function takeLatest(patternOrChannel, worker, ...args) {
185 return fork(takeLatestHelper, patternOrChannel, worker, ...args)
186}
187
188export function throttle(ms, pattern, worker, ...args) {
189 return fork(throttleHelper, ms, pattern, worker, ...args)
190}
191
192const createAsEffectType = type => effect => effect && effect[IO] && effect[type]
193
194export 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}