You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
772 lines
25 KiB
772 lines
25 KiB
'use strict';
|
|
|
|
exports.__esModule = true;
|
|
exports.TASK_CANCEL = exports.CHANNEL_END = exports.NOT_ITERATOR_ERROR = undefined;
|
|
|
|
var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };
|
|
|
|
var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; };
|
|
|
|
exports.default = proc;
|
|
|
|
var _utils = /*#__PURE__*/require('./utils');
|
|
|
|
var _scheduler = /*#__PURE__*/require('./scheduler');
|
|
|
|
var _io = /*#__PURE__*/require('./io');
|
|
|
|
var _channel = /*#__PURE__*/require('./channel');
|
|
|
|
var _buffers = /*#__PURE__*/require('./buffers');
|
|
|
|
function _defineEnumerableProperties(obj, descs) { for (var key in descs) { var desc = descs[key]; desc.configurable = desc.enumerable = true; if ("value" in desc) desc.writable = true; Object.defineProperty(obj, key, desc); } return obj; }
|
|
|
|
var NOT_ITERATOR_ERROR = exports.NOT_ITERATOR_ERROR = 'proc first argument (Saga function result) must be an iterator';
|
|
|
|
var CHANNEL_END = exports.CHANNEL_END = {
|
|
toString: function toString() {
|
|
return '@@redux-saga/CHANNEL_END';
|
|
}
|
|
};
|
|
var TASK_CANCEL = exports.TASK_CANCEL = {
|
|
toString: function toString() {
|
|
return '@@redux-saga/TASK_CANCEL';
|
|
}
|
|
};
|
|
|
|
var matchers = {
|
|
wildcard: function wildcard() {
|
|
return _utils.kTrue;
|
|
},
|
|
default: function _default(pattern) {
|
|
return (typeof pattern === 'undefined' ? 'undefined' : _typeof(pattern)) === 'symbol' ? function (input) {
|
|
return input.type === pattern;
|
|
} : function (input) {
|
|
return input.type === String(pattern);
|
|
};
|
|
},
|
|
array: function array(patterns) {
|
|
return function (input) {
|
|
return patterns.some(function (p) {
|
|
return matcher(p)(input);
|
|
});
|
|
};
|
|
},
|
|
predicate: function predicate(_predicate) {
|
|
return function (input) {
|
|
return _predicate(input);
|
|
};
|
|
}
|
|
};
|
|
|
|
function matcher(pattern) {
|
|
// prettier-ignore
|
|
return (pattern === '*' ? matchers.wildcard : _utils.is.array(pattern) ? matchers.array : _utils.is.stringableFunc(pattern) ? matchers.default : _utils.is.func(pattern) ? matchers.predicate : matchers.default)(pattern);
|
|
}
|
|
|
|
/**
|
|
Used to track a parent task and its forks
|
|
In the new fork model, forked tasks are attached by default to their parent
|
|
We model this using the concept of Parent task && main Task
|
|
main task is the main flow of the current Generator, the parent tasks is the
|
|
aggregation of the main tasks + all its forked tasks.
|
|
Thus the whole model represents an execution tree with multiple branches (vs the
|
|
linear execution tree in sequential (non parallel) programming)
|
|
|
|
A parent tasks has the following semantics
|
|
- It completes if all its forks either complete or all cancelled
|
|
- If it's cancelled, all forks are cancelled as well
|
|
- It aborts if any uncaught error bubbles up from forks
|
|
- If it completes, the return value is the one returned by the main task
|
|
**/
|
|
function forkQueue(name, mainTask, cb) {
|
|
var tasks = [],
|
|
result = void 0,
|
|
completed = false;
|
|
addTask(mainTask);
|
|
|
|
function abort(err) {
|
|
cancelAll();
|
|
cb(err, true);
|
|
}
|
|
|
|
function addTask(task) {
|
|
tasks.push(task);
|
|
task.cont = function (res, isErr) {
|
|
if (completed) {
|
|
return;
|
|
}
|
|
|
|
(0, _utils.remove)(tasks, task);
|
|
task.cont = _utils.noop;
|
|
if (isErr) {
|
|
abort(res);
|
|
} else {
|
|
if (task === mainTask) {
|
|
result = res;
|
|
}
|
|
if (!tasks.length) {
|
|
completed = true;
|
|
cb(result);
|
|
}
|
|
}
|
|
};
|
|
// task.cont.cancel = task.cancel
|
|
}
|
|
|
|
function cancelAll() {
|
|
if (completed) {
|
|
return;
|
|
}
|
|
completed = true;
|
|
tasks.forEach(function (t) {
|
|
t.cont = _utils.noop;
|
|
t.cancel();
|
|
});
|
|
tasks = [];
|
|
}
|
|
|
|
return {
|
|
addTask: addTask,
|
|
cancelAll: cancelAll,
|
|
abort: abort,
|
|
getTasks: function getTasks() {
|
|
return tasks;
|
|
},
|
|
taskNames: function taskNames() {
|
|
return tasks.map(function (t) {
|
|
return t.name;
|
|
});
|
|
}
|
|
};
|
|
}
|
|
|
|
function createTaskIterator(_ref) {
|
|
var context = _ref.context,
|
|
fn = _ref.fn,
|
|
args = _ref.args;
|
|
|
|
if (_utils.is.iterator(fn)) {
|
|
return fn;
|
|
}
|
|
|
|
// catch synchronous failures; see #152 and #441
|
|
var result = void 0,
|
|
error = void 0;
|
|
try {
|
|
result = fn.apply(context, args);
|
|
} catch (err) {
|
|
error = err;
|
|
}
|
|
|
|
// i.e. a generator function returns an iterator
|
|
if (_utils.is.iterator(result)) {
|
|
return result;
|
|
}
|
|
|
|
// do not bubble up synchronous failures for detached forks
|
|
// instead create a failed task. See #152 and #441
|
|
return error ? (0, _utils.makeIterator)(function () {
|
|
throw error;
|
|
}) : (0, _utils.makeIterator)(function () {
|
|
var pc = void 0;
|
|
var eff = { done: false, value: result };
|
|
var ret = function ret(value) {
|
|
return { done: true, value: value };
|
|
};
|
|
return function (arg) {
|
|
if (!pc) {
|
|
pc = true;
|
|
return eff;
|
|
} else {
|
|
return ret(arg);
|
|
}
|
|
};
|
|
}());
|
|
}
|
|
|
|
var wrapHelper = function wrapHelper(helper) {
|
|
return { fn: helper };
|
|
};
|
|
|
|
function proc(iterator) {
|
|
var subscribe = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : function () {
|
|
return _utils.noop;
|
|
};
|
|
var dispatch = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : _utils.noop;
|
|
var getState = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : _utils.noop;
|
|
var parentContext = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : {};
|
|
var options = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : {};
|
|
var parentEffectId = arguments.length > 6 && arguments[6] !== undefined ? arguments[6] : 0;
|
|
var name = arguments.length > 7 && arguments[7] !== undefined ? arguments[7] : 'anonymous';
|
|
var cont = arguments[8];
|
|
|
|
(0, _utils.check)(iterator, _utils.is.iterator, NOT_ITERATOR_ERROR);
|
|
|
|
var effectsString = '[...effects]';
|
|
var runParallelEffect = (0, _utils.deprecate)(runAllEffect, (0, _utils.updateIncentive)(effectsString, 'all(' + effectsString + ')'));
|
|
|
|
var sagaMonitor = options.sagaMonitor,
|
|
logger = options.logger,
|
|
onError = options.onError;
|
|
|
|
var log = logger || _utils.log;
|
|
var logError = function logError(err) {
|
|
var message = err.sagaStack;
|
|
|
|
if (!message && err.stack) {
|
|
message = err.stack.split('\n')[0].indexOf(err.message) !== -1 ? err.stack : 'Error: ' + err.message + '\n' + err.stack;
|
|
}
|
|
|
|
log('error', 'uncaught at ' + name, message || err.message || err);
|
|
};
|
|
var stdChannel = (0, _channel.stdChannel)(subscribe);
|
|
var taskContext = Object.create(parentContext);
|
|
/**
|
|
Tracks the current effect cancellation
|
|
Each time the generator progresses. calling runEffect will set a new value
|
|
on it. It allows propagating cancellation to child effects
|
|
**/
|
|
next.cancel = _utils.noop;
|
|
|
|
/**
|
|
Creates a new task descriptor for this generator, We'll also create a main task
|
|
to track the main flow (besides other forked tasks)
|
|
**/
|
|
var task = newTask(parentEffectId, name, iterator, cont);
|
|
var mainTask = { name: name, cancel: cancelMain, isRunning: true };
|
|
var taskQueue = forkQueue(name, mainTask, end);
|
|
|
|
/**
|
|
cancellation of the main task. We'll simply resume the Generator with a Cancel
|
|
**/
|
|
function cancelMain() {
|
|
if (mainTask.isRunning && !mainTask.isCancelled) {
|
|
mainTask.isCancelled = true;
|
|
next(TASK_CANCEL);
|
|
}
|
|
}
|
|
|
|
/**
|
|
This may be called by a parent generator to trigger/propagate cancellation
|
|
cancel all pending tasks (including the main task), then end the current task.
|
|
Cancellation propagates down to the whole execution tree holded by this Parent task
|
|
It's also propagated to all joiners of this task and their execution tree/joiners
|
|
Cancellation is noop for terminated/Cancelled tasks tasks
|
|
**/
|
|
function cancel() {
|
|
/**
|
|
We need to check both Running and Cancelled status
|
|
Tasks can be Cancelled but still Running
|
|
**/
|
|
if (iterator._isRunning && !iterator._isCancelled) {
|
|
iterator._isCancelled = true;
|
|
taskQueue.cancelAll();
|
|
/**
|
|
Ending with a Never result will propagate the Cancellation to all joiners
|
|
**/
|
|
end(TASK_CANCEL);
|
|
}
|
|
}
|
|
/**
|
|
attaches cancellation logic to this task's continuation
|
|
this will permit cancellation to propagate down the call chain
|
|
**/
|
|
cont && (cont.cancel = cancel);
|
|
|
|
// tracks the running status
|
|
iterator._isRunning = true;
|
|
|
|
// kicks up the generator
|
|
next();
|
|
|
|
// then return the task descriptor to the caller
|
|
return task;
|
|
|
|
/**
|
|
This is the generator driver
|
|
It's a recursive async/continuation function which calls itself
|
|
until the generator terminates or throws
|
|
**/
|
|
function next(arg, isErr) {
|
|
// Preventive measure. If we end up here, then there is really something wrong
|
|
if (!mainTask.isRunning) {
|
|
throw new Error('Trying to resume an already finished generator');
|
|
}
|
|
|
|
try {
|
|
var result = void 0;
|
|
if (isErr) {
|
|
result = iterator.throw(arg);
|
|
} else if (arg === TASK_CANCEL) {
|
|
/**
|
|
getting TASK_CANCEL automatically cancels the main task
|
|
We can get this value here
|
|
- By cancelling the parent task manually
|
|
- By joining a Cancelled task
|
|
**/
|
|
mainTask.isCancelled = true;
|
|
/**
|
|
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 = _utils.is.func(iterator.return) ? iterator.return(TASK_CANCEL) : { done: true, value: TASK_CANCEL };
|
|
} else if (arg === CHANNEL_END) {
|
|
// We get CHANNEL_END by taking from a channel that ended using `take` (and not `takem` used to trap End of channels)
|
|
result = _utils.is.func(iterator.return) ? iterator.return() : { done: true };
|
|
} else {
|
|
result = iterator.next(arg);
|
|
}
|
|
|
|
if (!result.done) {
|
|
runEffect(result.value, parentEffectId, '', next);
|
|
} else {
|
|
/**
|
|
This Generator has ended, terminate the main task and notify the fork queue
|
|
**/
|
|
mainTask.isMainRunning = false;
|
|
mainTask.cont && mainTask.cont(result.value);
|
|
}
|
|
} catch (error) {
|
|
if (mainTask.isCancelled) {
|
|
logError(error);
|
|
}
|
|
mainTask.isMainRunning = false;
|
|
mainTask.cont(error, true);
|
|
}
|
|
}
|
|
|
|
function end(result, isErr) {
|
|
iterator._isRunning = false;
|
|
stdChannel.close();
|
|
if (!isErr) {
|
|
iterator._result = result;
|
|
iterator._deferredEnd && iterator._deferredEnd.resolve(result);
|
|
} else {
|
|
if (result instanceof Error) {
|
|
Object.defineProperty(result, 'sagaStack', {
|
|
value: 'at ' + name + ' \n ' + (result.sagaStack || result.stack),
|
|
configurable: true
|
|
});
|
|
}
|
|
if (!task.cont) {
|
|
if (result instanceof Error && onError) {
|
|
onError(result);
|
|
} else {
|
|
logError(result);
|
|
}
|
|
}
|
|
iterator._error = result;
|
|
iterator._isAborted = true;
|
|
iterator._deferredEnd && iterator._deferredEnd.reject(result);
|
|
}
|
|
task.cont && task.cont(result, isErr);
|
|
task.joiners.forEach(function (j) {
|
|
return j.cb(result, isErr);
|
|
});
|
|
task.joiners = null;
|
|
}
|
|
|
|
function runEffect(effect, parentEffectId) {
|
|
var label = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : '';
|
|
var cb = arguments[3];
|
|
|
|
var effectId = (0, _utils.uid)();
|
|
sagaMonitor && sagaMonitor.effectTriggered({ effectId: effectId, parentEffectId: parentEffectId, label: label, effect: effect });
|
|
|
|
/**
|
|
completion callback and cancel callback are mutually exclusive
|
|
We can't cancel an already completed effect
|
|
And We can't complete an already cancelled effectId
|
|
**/
|
|
var effectSettled = void 0;
|
|
|
|
// Completion callback passed to the appropriate effect runner
|
|
function currCb(res, isErr) {
|
|
if (effectSettled) {
|
|
return;
|
|
}
|
|
|
|
effectSettled = true;
|
|
cb.cancel = _utils.noop; // defensive measure
|
|
if (sagaMonitor) {
|
|
isErr ? sagaMonitor.effectRejected(effectId, res) : sagaMonitor.effectResolved(effectId, res);
|
|
}
|
|
cb(res, isErr);
|
|
}
|
|
// tracks down the current cancel
|
|
currCb.cancel = _utils.noop;
|
|
|
|
// setup cancellation logic on the parent cb
|
|
cb.cancel = function () {
|
|
// prevents cancelling an already completed effect
|
|
if (effectSettled) {
|
|
return;
|
|
}
|
|
|
|
effectSettled = true;
|
|
/**
|
|
propagates cancel downward
|
|
catch uncaught cancellations errors; since we can no longer call the completion
|
|
callback, log errors raised during cancellations into the console
|
|
**/
|
|
try {
|
|
currCb.cancel();
|
|
} catch (err) {
|
|
logError(err);
|
|
}
|
|
currCb.cancel = _utils.noop; // defensive measure
|
|
|
|
sagaMonitor && sagaMonitor.effectCancelled(effectId);
|
|
};
|
|
|
|
/**
|
|
each effect runner must attach its own logic of cancellation to the provided callback
|
|
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
|
|
for 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
|
|
**/
|
|
var data = void 0;
|
|
// prettier-ignore
|
|
return (
|
|
// Non declarative effect
|
|
_utils.is.promise(effect) ? resolvePromise(effect, currCb) : _utils.is.helper(effect) ? runForkEffect(wrapHelper(effect), effectId, currCb) : _utils.is.iterator(effect) ? resolveIterator(effect, effectId, name, currCb)
|
|
|
|
// declarative effects
|
|
: _utils.is.array(effect) ? runParallelEffect(effect, effectId, currCb) : (data = _io.asEffect.take(effect)) ? runTakeEffect(data, currCb) : (data = _io.asEffect.put(effect)) ? runPutEffect(data, currCb) : (data = _io.asEffect.all(effect)) ? runAllEffect(data, effectId, currCb) : (data = _io.asEffect.race(effect)) ? runRaceEffect(data, effectId, currCb) : (data = _io.asEffect.call(effect)) ? runCallEffect(data, effectId, currCb) : (data = _io.asEffect.cps(effect)) ? runCPSEffect(data, currCb) : (data = _io.asEffect.fork(effect)) ? runForkEffect(data, effectId, currCb) : (data = _io.asEffect.join(effect)) ? runJoinEffect(data, currCb) : (data = _io.asEffect.cancel(effect)) ? runCancelEffect(data, currCb) : (data = _io.asEffect.select(effect)) ? runSelectEffect(data, currCb) : (data = _io.asEffect.actionChannel(effect)) ? runChannelEffect(data, currCb) : (data = _io.asEffect.flush(effect)) ? runFlushEffect(data, currCb) : (data = _io.asEffect.cancelled(effect)) ? runCancelledEffect(data, currCb) : (data = _io.asEffect.getContext(effect)) ? runGetContextEffect(data, currCb) : (data = _io.asEffect.setContext(effect)) ? runSetContextEffect(data, currCb) : /* anything else returned as is */currCb(effect)
|
|
);
|
|
}
|
|
|
|
function resolvePromise(promise, cb) {
|
|
var cancelPromise = promise[_utils.CANCEL];
|
|
if (_utils.is.func(cancelPromise)) {
|
|
cb.cancel = cancelPromise;
|
|
} else if (_utils.is.func(promise.abort)) {
|
|
cb.cancel = function () {
|
|
return promise.abort();
|
|
};
|
|
// TODO: add support for the fetch API, whenever they get around to
|
|
// adding cancel support
|
|
}
|
|
promise.then(cb, function (error) {
|
|
return cb(error, true);
|
|
});
|
|
}
|
|
|
|
function resolveIterator(iterator, effectId, name, cb) {
|
|
proc(iterator, subscribe, dispatch, getState, taskContext, options, effectId, name, cb);
|
|
}
|
|
|
|
function runTakeEffect(_ref2, cb) {
|
|
var channel = _ref2.channel,
|
|
pattern = _ref2.pattern,
|
|
maybe = _ref2.maybe;
|
|
|
|
channel = channel || stdChannel;
|
|
var takeCb = function takeCb(inp) {
|
|
return inp instanceof Error ? cb(inp, true) : (0, _channel.isEnd)(inp) && !maybe ? cb(CHANNEL_END) : cb(inp);
|
|
};
|
|
try {
|
|
channel.take(takeCb, matcher(pattern));
|
|
} catch (err) {
|
|
return cb(err, true);
|
|
}
|
|
cb.cancel = takeCb.cancel;
|
|
}
|
|
|
|
function runPutEffect(_ref3, cb) {
|
|
var channel = _ref3.channel,
|
|
action = _ref3.action,
|
|
resolve = _ref3.resolve;
|
|
|
|
/**
|
|
Schedule the put in case another saga is holding a lock.
|
|
The put will be executed atomically. ie nested puts will execute after
|
|
this put has terminated.
|
|
**/
|
|
(0, _scheduler.asap)(function () {
|
|
var result = void 0;
|
|
try {
|
|
result = (channel ? channel.put : dispatch)(action);
|
|
} catch (error) {
|
|
// If we have a channel or `put.resolve` was used then bubble up the error.
|
|
if (channel || resolve) return cb(error, true);
|
|
logError(error);
|
|
}
|
|
|
|
if (resolve && _utils.is.promise(result)) {
|
|
resolvePromise(result, cb);
|
|
} else {
|
|
return cb(result);
|
|
}
|
|
});
|
|
// Put effects are non cancellables
|
|
}
|
|
|
|
function runCallEffect(_ref4, effectId, cb) {
|
|
var context = _ref4.context,
|
|
fn = _ref4.fn,
|
|
args = _ref4.args;
|
|
|
|
var result = void 0;
|
|
// catch synchronous failures; see #152
|
|
try {
|
|
result = fn.apply(context, args);
|
|
} catch (error) {
|
|
return cb(error, true);
|
|
}
|
|
return _utils.is.promise(result) ? resolvePromise(result, cb) : _utils.is.iterator(result) ? resolveIterator(result, effectId, fn.name, cb) : cb(result);
|
|
}
|
|
|
|
function runCPSEffect(_ref5, cb) {
|
|
var context = _ref5.context,
|
|
fn = _ref5.fn,
|
|
args = _ref5.args;
|
|
|
|
// CPS (ie node style functions) can define their own cancellation logic
|
|
// by setting cancel field on the cb
|
|
|
|
// catch synchronous failures; see #152
|
|
try {
|
|
var cpsCb = function cpsCb(err, res) {
|
|
return _utils.is.undef(err) ? cb(res) : cb(err, true);
|
|
};
|
|
fn.apply(context, args.concat(cpsCb));
|
|
if (cpsCb.cancel) {
|
|
cb.cancel = function () {
|
|
return cpsCb.cancel();
|
|
};
|
|
}
|
|
} catch (error) {
|
|
return cb(error, true);
|
|
}
|
|
}
|
|
|
|
function runForkEffect(_ref6, effectId, cb) {
|
|
var context = _ref6.context,
|
|
fn = _ref6.fn,
|
|
args = _ref6.args,
|
|
detached = _ref6.detached;
|
|
|
|
var taskIterator = createTaskIterator({ context: context, fn: fn, args: args });
|
|
|
|
try {
|
|
(0, _scheduler.suspend)();
|
|
var _task = proc(taskIterator, subscribe, dispatch, getState, taskContext, options, effectId, fn.name, detached ? null : _utils.noop);
|
|
|
|
if (detached) {
|
|
cb(_task);
|
|
} else {
|
|
if (taskIterator._isRunning) {
|
|
taskQueue.addTask(_task);
|
|
cb(_task);
|
|
} else if (taskIterator._error) {
|
|
taskQueue.abort(taskIterator._error);
|
|
} else {
|
|
cb(_task);
|
|
}
|
|
}
|
|
} finally {
|
|
(0, _scheduler.flush)();
|
|
}
|
|
// Fork effects are non cancellables
|
|
}
|
|
|
|
function runJoinEffect(t, cb) {
|
|
if (t.isRunning()) {
|
|
var joiner = { task: task, cb: cb };
|
|
cb.cancel = function () {
|
|
return (0, _utils.remove)(t.joiners, joiner);
|
|
};
|
|
t.joiners.push(joiner);
|
|
} else {
|
|
t.isAborted() ? cb(t.error(), true) : cb(t.result());
|
|
}
|
|
}
|
|
|
|
function runCancelEffect(taskToCancel, cb) {
|
|
if (taskToCancel === _utils.SELF_CANCELLATION) {
|
|
taskToCancel = task;
|
|
}
|
|
if (taskToCancel.isRunning()) {
|
|
taskToCancel.cancel();
|
|
}
|
|
cb();
|
|
// cancel effects are non cancellables
|
|
}
|
|
|
|
function runAllEffect(effects, effectId, cb) {
|
|
var keys = Object.keys(effects);
|
|
|
|
if (!keys.length) {
|
|
return cb(_utils.is.array(effects) ? [] : {});
|
|
}
|
|
|
|
var completedCount = 0;
|
|
var completed = void 0;
|
|
var results = {};
|
|
var childCbs = {};
|
|
|
|
function checkEffectEnd() {
|
|
if (completedCount === keys.length) {
|
|
completed = true;
|
|
cb(_utils.is.array(effects) ? _utils.array.from(_extends({}, results, { length: keys.length })) : results);
|
|
}
|
|
}
|
|
|
|
keys.forEach(function (key) {
|
|
var chCbAtKey = function chCbAtKey(res, isErr) {
|
|
if (completed) {
|
|
return;
|
|
}
|
|
if (isErr || (0, _channel.isEnd)(res) || res === CHANNEL_END || res === TASK_CANCEL) {
|
|
cb.cancel();
|
|
cb(res, isErr);
|
|
} else {
|
|
results[key] = res;
|
|
completedCount++;
|
|
checkEffectEnd();
|
|
}
|
|
};
|
|
chCbAtKey.cancel = _utils.noop;
|
|
childCbs[key] = chCbAtKey;
|
|
});
|
|
|
|
cb.cancel = function () {
|
|
if (!completed) {
|
|
completed = true;
|
|
keys.forEach(function (key) {
|
|
return childCbs[key].cancel();
|
|
});
|
|
}
|
|
};
|
|
|
|
keys.forEach(function (key) {
|
|
return runEffect(effects[key], effectId, key, childCbs[key]);
|
|
});
|
|
}
|
|
|
|
function runRaceEffect(effects, effectId, cb) {
|
|
var completed = void 0;
|
|
var keys = Object.keys(effects);
|
|
var childCbs = {};
|
|
|
|
keys.forEach(function (key) {
|
|
var chCbAtKey = function chCbAtKey(res, isErr) {
|
|
if (completed) {
|
|
return;
|
|
}
|
|
|
|
if (isErr) {
|
|
// Race Auto cancellation
|
|
cb.cancel();
|
|
cb(res, true);
|
|
} else if (!(0, _channel.isEnd)(res) && res !== CHANNEL_END && res !== TASK_CANCEL) {
|
|
var _response;
|
|
|
|
cb.cancel();
|
|
completed = true;
|
|
var response = (_response = {}, _response[key] = res, _response);
|
|
cb(_utils.is.array(effects) ? [].slice.call(_extends({}, response, { length: keys.length })) : response);
|
|
}
|
|
};
|
|
chCbAtKey.cancel = _utils.noop;
|
|
childCbs[key] = chCbAtKey;
|
|
});
|
|
|
|
cb.cancel = function () {
|
|
// prevents unnecessary cancellation
|
|
if (!completed) {
|
|
completed = true;
|
|
keys.forEach(function (key) {
|
|
return childCbs[key].cancel();
|
|
});
|
|
}
|
|
};
|
|
keys.forEach(function (key) {
|
|
if (completed) {
|
|
return;
|
|
}
|
|
runEffect(effects[key], effectId, key, childCbs[key]);
|
|
});
|
|
}
|
|
|
|
function runSelectEffect(_ref7, cb) {
|
|
var selector = _ref7.selector,
|
|
args = _ref7.args;
|
|
|
|
try {
|
|
var state = selector.apply(undefined, [getState()].concat(args));
|
|
cb(state);
|
|
} catch (error) {
|
|
cb(error, true);
|
|
}
|
|
}
|
|
|
|
function runChannelEffect(_ref8, cb) {
|
|
var pattern = _ref8.pattern,
|
|
buffer = _ref8.buffer;
|
|
|
|
var match = matcher(pattern);
|
|
match.pattern = pattern;
|
|
cb((0, _channel.eventChannel)(subscribe, buffer || _buffers.buffers.fixed(), match));
|
|
}
|
|
|
|
function runCancelledEffect(data, cb) {
|
|
cb(!!mainTask.isCancelled);
|
|
}
|
|
|
|
function runFlushEffect(channel, cb) {
|
|
channel.flush(cb);
|
|
}
|
|
|
|
function runGetContextEffect(prop, cb) {
|
|
cb(taskContext[prop]);
|
|
}
|
|
|
|
function runSetContextEffect(props, cb) {
|
|
_utils.object.assign(taskContext, props);
|
|
cb();
|
|
}
|
|
|
|
function newTask(id, name, iterator, cont) {
|
|
var _done, _ref9, _mutatorMap;
|
|
|
|
iterator._deferredEnd = null;
|
|
return _ref9 = {}, _ref9[_utils.TASK] = true, _ref9.id = id, _ref9.name = name, _done = 'done', _mutatorMap = {}, _mutatorMap[_done] = _mutatorMap[_done] || {}, _mutatorMap[_done].get = function () {
|
|
if (iterator._deferredEnd) {
|
|
return iterator._deferredEnd.promise;
|
|
} else {
|
|
var def = (0, _utils.deferred)();
|
|
iterator._deferredEnd = def;
|
|
if (!iterator._isRunning) {
|
|
iterator._error ? def.reject(iterator._error) : def.resolve(iterator._result);
|
|
}
|
|
return def.promise;
|
|
}
|
|
}, _ref9.cont = cont, _ref9.joiners = [], _ref9.cancel = cancel, _ref9.isRunning = function isRunning() {
|
|
return iterator._isRunning;
|
|
}, _ref9.isCancelled = function isCancelled() {
|
|
return iterator._isCancelled;
|
|
}, _ref9.isAborted = function isAborted() {
|
|
return iterator._isAborted;
|
|
}, _ref9.result = function result() {
|
|
return iterator._result;
|
|
}, _ref9.error = function error() {
|
|
return iterator._error;
|
|
}, _ref9.setContext = function setContext(props) {
|
|
(0, _utils.check)(props, _utils.is.object, (0, _utils.createSetContextWarning)('task', props));
|
|
_utils.object.assign(taskContext, props);
|
|
}, _defineEnumerableProperties(_ref9, _mutatorMap), _ref9;
|
|
}
|
|
} |