Secure your code as it's written. Use Snyk Code to scan source code in minutes - no build needed - and fix issues immediately.
test('saga iteration', () => {
let actual = []
const middleware = sagaMiddleware()
createStore(() => ({}), {}, applyMiddleware(middleware))
function* genFn() {
actual.push(yield 1)
actual.push(yield 2)
return 3
}
const task = middleware.run(genFn) // saga should return a promise of the iterator result
expect(is.promise(task.toPromise())).toBe(true)
return task.toPromise().then(res => {
// saga's iterator should return false from isRunning()
expect(task.isRunning()).toBe(false) // saga returned promise should resolve with the iterator return value
expect(res).toBe(3) // saga should collect yielded values from the iterator
expect(actual).toEqual([1, 2])
})
})
test('saga error handling', () => {
if (providedValue !== value) {
return providedValue;
}
// Because we manually consume the `call`, we need to manually store
// the effect, so assertions on the `call` work.
processEffect({
effectId: nextSagaId(),
effect: value,
});
const { context, fn, args } = effect;
const result = fn.apply(context, args);
if (is.iterator(result)) {
return call(defaultSagaWrapper, result, refineYieldedValue);
}
return result;
}
// Ensure we wrap yielded iterators (i.e. `yield someInnerSaga()`) for
// providers to work.
case is.iterator(value):
return useProvidedValue(defaultSagaWrapper(value, refineYieldedValue));
default:
return useProvidedValue(value);
}
}
actual = args
}
const middleware = sagaMiddleware()
try {
middleware.run(function*() {})
} catch (e) {
// middleware.run must throw an Error when executed before the middleware is connected to a Store
expect(e instanceof Error).toBe(true)
}
createStore(() => {}, applyMiddleware(middleware))
const task = middleware.run(saga, 'argument') // middleware.run must return a Task Object
expect(is.task(task)).toBe(true)
const expected = ['argument'] // middleware must run the Saga and provides it with the given arguments
expect(actual).toEqual(expected)
})
export default function matcher(pattern) {
// prettier-ignore
const matcherCreator = (
pattern === '*' ? wildcard
: is.string(pattern) ? string
: is.array(pattern) ? array
: is.stringableFunc(pattern) ? string
: is.func(pattern) ? predicate
: is.symbol(pattern) ? symbol
: null
)
if (matcherCreator === null) {
throw new Error(`invalid pattern: ${pattern}`)
}
return matcherCreator(pattern)
}
- By cancelling the parent task manually
- By joining a Cancelled task
**/
mainTask.status = CANCELLED
/**
Cancels the current effect; this will propagate the cancellation down to any called tasks
**/
next.cancel()
/**
If this Generator has a `return` method then invokes it
This will jump to the finally block
**/
result = is.func(iterator.return) ? iterator.return(TASK_CANCEL) : { done: true, value: TASK_CANCEL }
} else if (shouldTerminate(arg)) {
// We get TERMINATE flag, i.e. by taking from a channel that ended using `take` (and not `takem` used to trap End of channels)
result = is.func(iterator.return) ? iterator.return() : { done: true }
} else {
result = iterator.next(arg)
}
if (!result.done) {
digestEffect(result.value, parentEffectId, next)
} else {
/**
This Generator has ended, terminate the main task and notify the fork queue
**/
if (mainTask.status !== CANCELLED) {
mainTask.status = DONE
}
mainTask.cont(result.value)
}
} catch (error) {
function runCancelEffect(env, taskOrTasks, cb, { task }) {
if (taskOrTasks === SELF_CANCELLATION) {
cancelSingleTask(task)
} else if (is.array(taskOrTasks)) {
taskOrTasks.forEach(cancelSingleTask)
} else {
cancelSingleTask(taskOrTasks)
}
cb()
// cancel effects are non cancellables
}
export default function matcher(pattern) {
// prettier-ignore
const matcherCreator = (
pattern === '*' ? wildcard
: is.string(pattern) ? string
: is.array(pattern) ? array
: is.stringableFunc(pattern) ? string
: is.func(pattern) ? predicate
: is.symbol(pattern) ? symbol
: null
)
if (matcherCreator === null) {
throw new Error(`invalid pattern: ${pattern}`)
}
return matcherCreator(pattern)
}
function createTaskIterator({ context, fn, args }) {
// catch synchronous failures; see #152 and #441
try {
const result = fn.apply(context, args)
// i.e. a generator function returns an iterator
if (is.iterator(result)) {
return result
}
let resolved = false
const next = arg => {
if (!resolved) {
resolved = true
// Only promises returned from fork will be interpreted. See #1573
return { value: result, done: !is.promise(result) }
} else {
return { value: arg, done: true }
}
}
return makeIterator(next)
it allows this generator to propagate cancellation downward.
ATTENTION! effect runners must setup the cancel logic by setting cb.cancel = [cancelMethod]
And the setup must occur before calling the callback
This is a sort of inversion of control: called async functions are responsible
of completing the flow by calling the provided continuation; while caller functions
are responsible for aborting the current flow by calling the attached cancel function
Library users can attach their own cancellation logic to promises by defining a
promise[CANCEL] method in their returned promises
ATTENTION! calling cancel must have no effect on an already completed or cancelled effect
**/
if (is.promise(effect)) {
resolvePromise(effect, currCb)
} else if (is.iterator(effect)) {
// resolve iterator
proc(env, effect, task.context, effectId, meta, /* isRoot */ false, currCb)
} else if (effect && effect[IO]) {
const effectRunner = effectRunnerMap[effect.type]
effectRunner(env, effect.payload, currCb, executingContext)
} else {
// anything else returned as is
currCb(effect)
}
}
function runCallEffect(env, { context, fn, args }, cb, { task }) {
// catch synchronous failures; see #152
try {
const result = fn.apply(context, args)
if (is.promise(result)) {
resolvePromise(result, cb)
return
}
if (is.iterator(result)) {
// resolve iterator
proc(env, result, task.context, currentEffectId, getMetaInfo(fn), /* isRoot */ false, cb)
return
}
cb(result)
} catch (error) {
cb(error, true)
}
}