redux-saga
Advanced tools
Comparing version 0.5.0 to 0.6.0
@@ -62,10 +62,10 @@ (function webpackUniversalModuleDefinition(root, factory) { | ||
}); | ||
exports.monitorActions = exports.storeIO = exports.runSaga = exports.as = exports.cancel = exports.join = exports.fork = exports.cps = exports.apply = exports.call = exports.race = exports.put = exports.take = exports.is = exports.MANUAL_CANCEL = exports.PARALLEL_AUTO_CANCEL = exports.RACE_AUTO_CANCEL = exports.SagaCancellationException = undefined; | ||
exports.monitorActions = exports.storeIO = exports.runSaga = exports.as = exports.cancel = exports.join = exports.fork = exports.cps = exports.apply = exports.call = exports.race = exports.put = exports.take = exports.is = exports.SagaCancellationException = exports.MANUAL_CANCEL = exports.PARALLEL_AUTO_CANCEL = exports.RACE_AUTO_CANCEL = exports.CANCEL = undefined; | ||
var _proc = __webpack_require__(2); | ||
var _proc = __webpack_require__(3); | ||
Object.defineProperty(exports, 'SagaCancellationException', { | ||
Object.defineProperty(exports, 'CANCEL', { | ||
enumerable: true, | ||
get: function get() { | ||
return _proc.SagaCancellationException; | ||
return _proc.CANCEL; | ||
} | ||
@@ -101,3 +101,3 @@ }); | ||
var _io = __webpack_require__(4); | ||
var _io = __webpack_require__(6); | ||
@@ -165,3 +165,3 @@ Object.defineProperty(exports, 'take', { | ||
var _runSaga = __webpack_require__(7); | ||
var _runSaga = __webpack_require__(8); | ||
@@ -181,8 +181,12 @@ Object.defineProperty(exports, 'runSaga', { | ||
var _middleware = __webpack_require__(6); | ||
var _middleware = __webpack_require__(7); | ||
var _middleware2 = _interopRequireDefault(_middleware); | ||
var _monitorActions = __webpack_require__(5); | ||
var _SagaCancellationException2 = __webpack_require__(4); | ||
var _SagaCancellationException3 = _interopRequireDefault(_SagaCancellationException2); | ||
var _monitorActions = __webpack_require__(2); | ||
var monitorActions = _interopRequireWildcard(_monitorActions); | ||
@@ -195,2 +199,4 @@ | ||
exports.default = _middleware2.default; | ||
var SagaCancellationException = exports.SagaCancellationException = _SagaCancellationException3.default; | ||
exports.monitorActions = monitorActions; | ||
@@ -200,5 +206,5 @@ | ||
/* 1 */ | ||
/***/ function(module, exports) { | ||
/***/ function(module, exports, __webpack_require__) { | ||
'use strict'; | ||
/* WEBPACK VAR INJECTION */(function(process) {'use strict'; | ||
@@ -222,2 +228,4 @@ 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 isDev = exports.isDev = typeof process !== 'undefined' && process.env && ("development") === 'development'; | ||
function check(value, predicate, error) { | ||
@@ -290,5 +298,44 @@ if (!predicate(value)) throw new Error(error); | ||
} | ||
/* WEBPACK VAR INJECTION */}.call(exports, __webpack_require__(9))) | ||
/***/ }, | ||
/* 2 */ | ||
/***/ function(module, exports) { | ||
'use strict'; | ||
Object.defineProperty(exports, "__esModule", { | ||
value: true | ||
}); | ||
exports.effectTriggered = effectTriggered; | ||
exports.effectResolved = effectResolved; | ||
exports.effectRejected = effectRejected; | ||
function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } | ||
var MONITOR_ACTION = exports.MONITOR_ACTION = 'MONITOR_ACTION'; | ||
var EFFECT_TRIGGERED = exports.EFFECT_TRIGGERED = 'EFFECT_TRIGGERED'; | ||
var EFFECT_RESOLVED = exports.EFFECT_RESOLVED = 'EFFECT_RESOLVED'; | ||
var EFFECT_REJECTED = exports.EFFECT_REJECTED = 'EFFECT_REJECTED'; | ||
function effectTriggered(effectId, parentEffectId, label, effect) { | ||
var _ref; | ||
return _ref = {}, _defineProperty(_ref, MONITOR_ACTION, true), _defineProperty(_ref, 'type', EFFECT_TRIGGERED), _defineProperty(_ref, 'effectId', effectId), _defineProperty(_ref, 'parentEffectId', parentEffectId), _defineProperty(_ref, 'label', label), _defineProperty(_ref, 'effect', effect), _ref; | ||
} | ||
function effectResolved(effectId, result) { | ||
var _ref2; | ||
return _ref2 = {}, _defineProperty(_ref2, MONITOR_ACTION, true), _defineProperty(_ref2, 'type', EFFECT_RESOLVED), _defineProperty(_ref2, 'effectId', effectId), _defineProperty(_ref2, 'result', result), _ref2; | ||
} | ||
function effectRejected(effectId, error) { | ||
var _ref3; | ||
return _ref3 = {}, _defineProperty(_ref3, MONITOR_ACTION, true), _defineProperty(_ref3, 'type', EFFECT_REJECTED), _defineProperty(_ref3, 'effectId', effectId), _defineProperty(_ref3, 'error', error), _ref3; | ||
} | ||
/***/ }, | ||
/* 3 */ | ||
/***/ function(module, exports, __webpack_require__) { | ||
@@ -301,3 +348,3 @@ | ||
}); | ||
exports.SagaCancellationException = exports.CANCEL = exports.MANUAL_CANCEL = exports.RACE_AUTO_CANCEL = exports.PARALLEL_AUTO_CANCEL = exports.NOT_ITERATOR_ERROR = undefined; | ||
exports.MANUAL_CANCEL = exports.RACE_AUTO_CANCEL = exports.PARALLEL_AUTO_CANCEL = exports.CANCEL = exports.NOT_ITERATOR_ERROR = undefined; | ||
exports.default = proc; | ||
@@ -307,8 +354,14 @@ | ||
var _io = __webpack_require__(4); | ||
var _io = __webpack_require__(6); | ||
var _monitorActions = __webpack_require__(5); | ||
var _monitorActions = __webpack_require__(2); | ||
var monitorActions = _interopRequireWildcard(_monitorActions); | ||
var _SagaCancellationException = __webpack_require__(4); | ||
var _SagaCancellationException2 = _interopRequireDefault(_SagaCancellationException); | ||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } | ||
function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } } | ||
@@ -318,5 +371,4 @@ | ||
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } | ||
var NOT_ITERATOR_ERROR = exports.NOT_ITERATOR_ERROR = 'proc first argument (Saga function result) must be an iterator'; | ||
var CANCEL = exports.CANCEL = Symbol('@@redux-saga/cancelPromise'); | ||
var PARALLEL_AUTO_CANCEL = exports.PARALLEL_AUTO_CANCEL = 'PARALLEL_AUTO_CANCEL'; | ||
@@ -328,12 +380,2 @@ var RACE_AUTO_CANCEL = exports.RACE_AUTO_CANCEL = 'RACE_AUTO_CANCEL'; | ||
var CANCEL = exports.CANCEL = Symbol('@@redux-saga/cancelPromise'); | ||
var SagaCancellationException = exports.SagaCancellationException = function SagaCancellationException(type, saga, origin) { | ||
_classCallCheck(this, SagaCancellationException); | ||
this.type = type; | ||
this.saga = saga; | ||
this.origin = origin; | ||
}; | ||
function proc(iterator) { | ||
@@ -350,28 +392,72 @@ var subscribe = arguments.length <= 1 || arguments[1] === undefined ? function () { | ||
// tracks the current `take` effects | ||
var deferredInputs = []; | ||
var canThrow = _utils.is.throw(iterator); | ||
// Promise to be resolved/rejected when this generator terminates (or throws) | ||
var deferredEnd = (0, _utils.deferred)(); | ||
// subscribe to input events, this will resolve the current `take` effects | ||
var unsubscribe = subscribe(function (input) { | ||
deferredInputs.forEach(function (def) { | ||
if (def.match(input)) def.resolve(input); | ||
}); | ||
for (var i = 0; i < deferredInputs.length; i++) { | ||
var def = deferredInputs[i]; | ||
if (def.match(input)) { | ||
// cancel all deferredInputs; parallel takes are disallowed | ||
// and in concurrent takes, first wins | ||
deferredInputs = []; | ||
def.resolve(input); | ||
} | ||
} | ||
}); | ||
/** | ||
cancel : (SagaCancellationException) -> () | ||
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 | ||
**/ | ||
var task = newTask(parentEffectId, name, iterator, deferredEnd.promise); | ||
/** | ||
this maybe called by a parent generator to trigger/propagate cancellation | ||
W'll simply cancel the current effect, which will reject that effect | ||
The rejection will throw the injected SagaCancellationException into the flow | ||
of this generator | ||
**/ | ||
task.done[CANCEL] = function (_ref) { | ||
var type = _ref.type; | ||
var origin = _ref.origin; | ||
next.cancel(new _SagaCancellationException2.default(type, name, origin)); | ||
}; | ||
// tracks the running status | ||
iterator._isRunning = true; | ||
// kicks up the generator | ||
next(); | ||
return newTask(parentEffectId, name, iterator, deferredEnd.promise); | ||
// then return the task descriptor to the caller | ||
return task; | ||
function next(arg, isError) { | ||
if (!iterator._isRunning) return; | ||
/** | ||
This is the generator driver | ||
It's a recursive aysnc/continuation function which calls itself | ||
until the generator terminates or throws | ||
**/ | ||
function next(error, arg) { | ||
// Preventive measure. If we endup here, then there is really something wrong | ||
if (!iterator._isRunning) throw new Error('Trying to resume an already finished generator'); | ||
try { | ||
if (isError && !canThrow) throw arg; | ||
var result = isError ? iterator.throw(arg) : iterator.next(arg); | ||
if (error && !canThrow) throw error; | ||
// calling iterator.throw on a generator that doesnt defined a correponding try/Catch | ||
var result = error ? iterator.throw(error) : iterator.next(arg); | ||
if (!result.done) { | ||
var currentEffect = runEffect(result.value, parentEffectId); | ||
deferredEnd.promise[CANCEL] = currentEffect[CANCEL]; | ||
currentEffect.then(next, function (err) { | ||
return next(err, true); | ||
}); | ||
runEffect(result.value, parentEffectId, '', next); | ||
} else { | ||
@@ -382,3 +468,5 @@ end(result.value); | ||
/*eslint-disable no-console*/ | ||
console.warn(name + ': uncaught', error); | ||
if (_utils.isDev) { | ||
console.warn(name + ': uncaught', error); | ||
} | ||
end(error, true); | ||
@@ -402,2 +490,3 @@ } | ||
var label = arguments.length <= 2 || arguments[2] === undefined ? '' : arguments[2]; | ||
var cb = arguments[3]; | ||
@@ -407,54 +496,111 @@ var effectId = nextEffectId(); | ||
var data = undefined; | ||
var promise = _utils.is.array(effect) ? runParallelEffect(effect, effectId) : _utils.is.iterator(effect) ? proc(effect, subscribe, dispatch, monitor, effectId).done : (data = _io.as.take(effect)) ? runTakeEffect(data) : (data = _io.as.put(effect)) ? runPutEffect(data) : (data = _io.as.race(effect)) ? runRaceEffect(data, effectId) : (data = _io.as.call(effect)) ? runCallEffect(data, effectId) : (data = _io.as.cps(effect)) ? runCPSEffect(data) : (data = _io.as.fork(effect)) ? runForkEffect(data, effectId) : (data = _io.as.join(effect)) ? runJoinEffect(data) : (data = _io.as.cancel(effect)) ? runCancelEffect(data) : /* resolve anything else */Promise.resolve(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 = undefined; | ||
var def = (0, _utils.deferred)(); | ||
var isRunning = true; | ||
var completeWith = function completeWith(fn) { | ||
return function (outcome) { | ||
if (isRunning) { | ||
isRunning = false; | ||
fn(outcome); | ||
} | ||
}; | ||
}; | ||
promise.then(completeWith(def.resolve), completeWith(def.reject)); | ||
def.promise[CANCEL] = function (_ref) { | ||
var type = _ref.type; | ||
var origin = _ref.origin; | ||
// Completion callback passed to the appropriate effect runner | ||
function currCb(err, res) { | ||
if (effectSettled) return; | ||
if (isRunning) { | ||
isRunning = false; | ||
var error = new SagaCancellationException(type, name, origin); | ||
cancelPromise(promise, error); | ||
def.reject(error); | ||
effectSettled = true; | ||
cb.cancel = _utils.noop; // defensive measure | ||
err ? monitor(monitorActions.effectRejected(effectId, err)) : monitor(monitorActions.effectResolved(effectId, res)); | ||
cb(err, res); | ||
} | ||
// tracks down the current cancel | ||
currCb.cancel = _utils.noop; | ||
// setup cancellation logic on the parent cb | ||
cb.cancel = function (cancelError) { | ||
// prevents cancelling an already completed effect | ||
if (effectSettled) return; | ||
effectSettled = true; | ||
/** | ||
propagates cancel downward | ||
catch uncaught cancellations errors, | ||
because w'll throw our own cancellation error inside this generator | ||
**/ | ||
try { | ||
currCb.cancel(cancelError); | ||
} catch (err) { | ||
void 0; | ||
} | ||
currCb.cancel = _utils.noop; // defensive measure | ||
/** | ||
triggers/propagates the cancellation error | ||
**/ | ||
cb(cancelError); | ||
monitor(monitorActions.effectRejected(effectId, cancelError)); | ||
}; | ||
def.promise.then(function (result) { | ||
return monitor(monitorActions.effectResolved(effectId, result)); | ||
/** | ||
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 | ||
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 | ||
**/ | ||
var data = undefined; | ||
return( | ||
// Non declarative effect | ||
_utils.is.promise(effect) ? resolvePromise(effect, currCb) : _utils.is.iterator(effect) ? resolveIterator(effect, effectId, name, currCb) | ||
// declarative effects | ||
: _utils.is.array(effect) ? runParallelEffect(effect, effectId, currCb) : _utils.is.notUndef(data = _io.as.take(effect)) ? runTakeEffect(data, currCb) : _utils.is.notUndef(data = _io.as.put(effect)) ? runPutEffect(data, currCb) : _utils.is.notUndef(data = _io.as.race(effect)) ? runRaceEffect(data, effectId, currCb) : _utils.is.notUndef(data = _io.as.call(effect)) ? runCallEffect(data, effectId, currCb) : _utils.is.notUndef(data = _io.as.cps(effect)) ? runCPSEffect(data, currCb) : _utils.is.notUndef(data = _io.as.fork(effect)) ? runForkEffect(data, effectId, currCb) : _utils.is.notUndef(data = _io.as.join(effect)) ? runJoinEffect(data, currCb) : _utils.is.notUndef(data = _io.as.cancel(effect)) ? runCancelEffect(data, currCb) : /* anything else returned as is */currCb(null, effect) | ||
); | ||
} | ||
function resolvePromise(promise, cb) { | ||
var cancelPromise = promise[CANCEL]; | ||
if (typeof cancelPromise === 'function') { | ||
cb.cancel = cancelPromise; | ||
} | ||
promise.then(function (result) { | ||
return cb(null, result); | ||
}, function (error) { | ||
return monitor(monitorActions.effectRejected(effectId, error)); | ||
return cb(error); | ||
}); | ||
return def.promise; | ||
} | ||
function runTakeEffect(pattern) { | ||
var def = (0, _utils.deferred)({ match: (0, _io.matcher)(pattern), pattern: pattern }); | ||
function resolveIterator(iterator, effectId, name, cb) { | ||
resolvePromise(proc(iterator, subscribe, dispatch, monitor, effectId, name).done, cb); | ||
} | ||
function runTakeEffect(pattern, cb) { | ||
var def = { | ||
match: (0, _io.matcher)(pattern), | ||
pattern: pattern, | ||
resolve: function resolve(input) { | ||
return cb(null, input); | ||
} | ||
}; | ||
deferredInputs.push(def); | ||
var done = function done() { | ||
// cancellation logic for take effect | ||
cb.cancel = function () { | ||
return (0, _utils.remove)(deferredInputs, def); | ||
}; | ||
def.promise.then(done, done); | ||
def.promise[CANCEL] = done; | ||
return def.promise; | ||
} | ||
function runPutEffect(action) { | ||
return (0, _utils.asap)(function () { | ||
return dispatch(action); | ||
function runPutEffect(action, cb) { | ||
// TODO check why synchronously nested dispatches aren't forwarded to the store | ||
// For now, this workaround allows the dispatch to occur on the next microtask | ||
// But could have side effect on a sync interleaved take/dispatch flow (ARGGHHHHHHH) | ||
(0, _utils.asap)(function () { | ||
return cb(null, dispatch(action)); | ||
}); | ||
// Put effects are non cancellables | ||
} | ||
function runCallEffect(_ref2, effectId) { | ||
function runCallEffect(_ref2, effectId, cb) { | ||
var context = _ref2.context; | ||
@@ -465,6 +611,6 @@ var fn = _ref2.fn; | ||
var result = fn.apply(context, args); | ||
return !_utils.is.iterator(result) ? Promise.resolve(result) : proc(result, subscribe, dispatch, monitor, effectId, fn.name).done; | ||
return _utils.is.promise(result) ? resolvePromise(result, cb) : _utils.is.iterator(result) ? resolveIterator(result, effectId, fn.name, cb) : cb(null, result); | ||
} | ||
function runCPSEffect(_ref3) { | ||
function runCPSEffect(_ref3, cb) { | ||
var context = _ref3.context; | ||
@@ -474,10 +620,8 @@ var fn = _ref3.fn; | ||
return new Promise(function (resolve, reject) { | ||
fn.apply(context, args.concat(function (err, res) { | ||
return _utils.is.undef(err) ? resolve(res) : reject(err); | ||
})); | ||
}); | ||
// CPS (ie node style functions) can define their own cancellation logic | ||
// by setting cancel field on the cb | ||
fn.apply(context, args.concat(cb)); | ||
} | ||
function runForkEffect(_ref4, effectId) { | ||
function runForkEffect(_ref4, effectId, cb) { | ||
var context = _ref4.context; | ||
@@ -521,75 +665,168 @@ var fn = _ref4.fn; | ||
return Promise.resolve(proc(_iterator, subscribe, dispatch, monitor, effectId, fn.name, true)); | ||
cb(null, proc(_iterator, subscribe, dispatch, monitor, effectId, fn.name, true)); | ||
// Fork effects are non cancellables | ||
} | ||
function runJoinEffect(task) { | ||
return task.done; | ||
function runJoinEffect(task, cb) { | ||
resolvePromise(task.done, cb); | ||
} | ||
function runCancelEffect(task) { | ||
task.done[CANCEL](new SagaCancellationException(MANUAL_CANCEL, '', name)); | ||
return Promise.resolve(); | ||
function runCancelEffect(task, cb) { | ||
// cancel the given task | ||
// uncaught cancellations errors bubbles upward | ||
task.done[CANCEL](new _SagaCancellationException2.default(MANUAL_CANCEL, name, name)); | ||
cb(); | ||
// cancel effects are non cancellables | ||
} | ||
function runParallelEffect(effects, effectId) { | ||
var promises = effects.map(function (eff) { | ||
return runEffect(eff, effectId); | ||
// Reimplementing Promise.all. We're in 2016 | ||
function runParallelEffect(effects, effectId, cb) { | ||
var completedCount = 0; | ||
var completed = undefined; | ||
var results = Array(effects.length); | ||
var childCbs = effects.map(function (eff, idx) { | ||
var chCbAtIdx = function chCbAtIdx(err, res) { | ||
// Either we've been cancelled, or an error aborted the whole effect | ||
if (completed) return; | ||
// one of the effects failed | ||
if (err) { | ||
// cancel all other effects | ||
// This is an AUTO_CANCEL (not triggered by a manual cancel) | ||
// Catch uncaught cancellation errors, because w'll only throw the actual | ||
// rejection error (err) inside this generator | ||
try { | ||
cb.cancel(new _SagaCancellationException2.default(PARALLEL_AUTO_CANCEL, name, name)); | ||
} catch (err) { | ||
void 0; | ||
} | ||
cb(err); | ||
} else { | ||
results[idx] = res; | ||
completedCount++; | ||
if (completedCount === results.length) { | ||
completed = true; | ||
cb(null, results); | ||
} | ||
} | ||
}; | ||
chCbAtIdx.cancel = _utils.noop; | ||
return chCbAtIdx; | ||
}); | ||
var ret = Promise.all(promises); | ||
ret[CANCEL] = function (error) { | ||
promises.forEach(function (p) { | ||
return cancelPromise(p, error); | ||
}); | ||
// This is different, a cancellation coming from upward | ||
// either a MANUAL_CANCEL or a parent AUTO_CANCEL | ||
// No need to catch, will be swallowed by the caller | ||
cb.cancel = function (cancelError) { | ||
// prevents unnecessary cancellation | ||
if (!completed) { | ||
completed = true; | ||
childCbs.forEach(function (chCb) { | ||
return chCb.cancel(cancelError); | ||
}); | ||
} | ||
}; | ||
ret.catch(function () { | ||
ret[CANCEL](new SagaCancellationException(PARALLEL_AUTO_CANCEL, name, name)); | ||
effects.forEach(function (eff, idx) { | ||
return runEffect(eff, effectId, idx, childCbs[idx]); | ||
}); | ||
return ret; | ||
} | ||
function runRaceEffect(effects, effectId) { | ||
var promises = []; | ||
var retP = Promise.race(Object.keys(effects).map(function (key) { | ||
var promise = runEffect(effects[key], effectId, key); | ||
promises.push(promise); | ||
return promise.then(function (result) { | ||
return _defineProperty({}, key, result); | ||
}, function (error) { | ||
return Promise.reject(_defineProperty({}, key, error)); | ||
}); | ||
})); | ||
// And yet; Promise.race | ||
function runRaceEffect(effects, effectId, cb) { | ||
var completed = undefined; | ||
var keys = Object.keys(effects); | ||
var childCbs = {}; | ||
retP[CANCEL] = function (error) { | ||
promises.forEach(function (p) { | ||
return cancelPromise(p, error); | ||
}); | ||
}; | ||
keys.forEach(function (key) { | ||
var chCbAtKey = function chCbAtKey(err, res) { | ||
// Either we've been cancelled, or an error aborted the whole effect | ||
if (completed) return; | ||
var done = function done() { | ||
return retP[CANCEL](new SagaCancellationException(RACE_AUTO_CANCEL, name, name)); | ||
if (err) { | ||
// Race Auto cancellation | ||
try { | ||
cb.cancel(new _SagaCancellationException2.default(RACE_AUTO_CANCEL, name, name)); | ||
} catch (err) { | ||
void 0; | ||
} | ||
cb(_defineProperty({}, key, err)); | ||
} else { | ||
try { | ||
cb.cancel(new _SagaCancellationException2.default(RACE_AUTO_CANCEL, name, name)); | ||
} catch (err) { | ||
void 0; | ||
} | ||
completed = true; | ||
cb(null, _defineProperty({}, key, res)); | ||
} | ||
}; | ||
chCbAtKey.cancel = _utils.noop; | ||
childCbs[key] = chCbAtKey; | ||
}); | ||
cb.cancel = function (cancelError) { | ||
// prevents unnecessary cancellation | ||
if (!completed) { | ||
completed = true; | ||
keys.forEach(function (key) { | ||
return childCbs[key].cancel(cancelError); | ||
}); | ||
} | ||
}; | ||
retP.then(done, done); | ||
return retP; | ||
keys.forEach(function (key) { | ||
return runEffect(effects[key], effectId, key, childCbs[key]); | ||
}); | ||
} | ||
function newTask(id, name, iterator, done, forked) { | ||
var _ref6; | ||
var _ref5; | ||
return _ref6 = {}, _defineProperty(_ref6, _utils.TASK, true), _defineProperty(_ref6, 'id', id), _defineProperty(_ref6, 'name', name), _defineProperty(_ref6, 'done', done), _defineProperty(_ref6, 'forked', forked), _defineProperty(_ref6, 'isRunning', function isRunning() { | ||
return _ref5 = {}, _defineProperty(_ref5, _utils.TASK, true), _defineProperty(_ref5, 'id', id), _defineProperty(_ref5, 'name', name), _defineProperty(_ref5, 'done', done), _defineProperty(_ref5, 'forked', forked), _defineProperty(_ref5, 'isRunning', function isRunning() { | ||
return iterator._isRunning; | ||
}), _defineProperty(_ref6, 'getResult', function getResult() { | ||
}), _defineProperty(_ref5, 'getResult', function getResult() { | ||
return iterator._result; | ||
}), _defineProperty(_ref6, 'getError', function getError() { | ||
}), _defineProperty(_ref5, 'getError', function getError() { | ||
return iterator._error; | ||
}), _ref6; | ||
}), _ref5; | ||
} | ||
} | ||
function cancelPromise(promise, error) { | ||
if (promise[CANCEL]) promise[CANCEL](error); | ||
} | ||
/***/ }, | ||
/* 4 */ | ||
/***/ function(module, exports) { | ||
'use strict'; | ||
Object.defineProperty(exports, "__esModule", { | ||
value: true | ||
}); | ||
exports.default = SagaCancellationException; | ||
/** | ||
* Creates an instance of a cancellation error | ||
* used internally by the Library to handle Cancellations effects | ||
* params: | ||
* type: PARALLEL_AUTO_CANCEL | RACE_AUTO_CANCEL | MANUAL_CANCEL | ||
* saga: current saga where the cancellation is to be thrown | ||
* origin: Origin saga from which the cancellation originated | ||
*/ | ||
function SagaCancellationException(type, saga, origin) { | ||
var message = 'SagaCancellationException; type: ' + type + ', saga: ' + saga + ', origin: ' + origin; | ||
this.name = 'SagaCancellationException'; | ||
this.message = message; | ||
this.type = type; | ||
this.saga = saga; | ||
this.origin = origin; | ||
this.stack = new Error().stack; | ||
} | ||
SagaCancellationException.prototype = Object.create(Error.prototype); | ||
SagaCancellationException.prototype.constructor = SagaCancellationException; | ||
/***/ }, | ||
/* 3 */ | ||
/* 5 */ | ||
/***/ function(module, exports, __webpack_require__) { | ||
@@ -630,3 +867,3 @@ | ||
/***/ }, | ||
/* 4 */ | ||
/* 6 */ | ||
/***/ function(module, exports, __webpack_require__) { | ||
@@ -817,40 +1054,3 @@ | ||
/***/ }, | ||
/* 5 */ | ||
/***/ function(module, exports) { | ||
'use strict'; | ||
Object.defineProperty(exports, "__esModule", { | ||
value: true | ||
}); | ||
exports.effectTriggered = effectTriggered; | ||
exports.effectResolved = effectResolved; | ||
exports.effectRejected = effectRejected; | ||
var EFFECT_TRIGGERED = exports.EFFECT_TRIGGERED = 'EFFECT_TRIGGERED'; | ||
var EFFECT_RESOLVED = exports.EFFECT_RESOLVED = 'EFFECT_RESOLVED'; | ||
var EFFECT_REJECTED = exports.EFFECT_REJECTED = 'EFFECT_REJECTED'; | ||
function effectTriggered(effectId, parentEffectId, label, effect) { | ||
return { | ||
type: EFFECT_TRIGGERED, | ||
effectId: effectId, parentEffectId: parentEffectId, label: label, effect: effect | ||
}; | ||
} | ||
function effectResolved(effectId, result) { | ||
return { | ||
type: EFFECT_RESOLVED, | ||
effectId: effectId, result: result | ||
}; | ||
} | ||
function effectRejected(effectId, error) { | ||
return { | ||
type: EFFECT_REJECTED, | ||
effectId: effectId, error: error | ||
}; | ||
} | ||
/***/ }, | ||
/* 6 */ | ||
/* 7 */ | ||
/***/ function(module, exports, __webpack_require__) { | ||
@@ -866,10 +1066,12 @@ | ||
var _proc = __webpack_require__(2); | ||
var _proc = __webpack_require__(3); | ||
var _proc2 = _interopRequireDefault(_proc); | ||
var _emitter = __webpack_require__(3); | ||
var _emitter = __webpack_require__(5); | ||
var _emitter2 = _interopRequireDefault(_emitter); | ||
var _monitorActions = __webpack_require__(2); | ||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } | ||
@@ -887,9 +1089,10 @@ | ||
var sagaEmitter = (0, _emitter2.default)(); | ||
var monitor = _utils.isDev ? function (action) { | ||
return (0, _utils.asap)(function () { | ||
return dispatch(action); | ||
}); | ||
} : undefined; | ||
sagas.forEach(function (saga) { | ||
(0, _proc2.default)(saga(getState), sagaEmitter.subscribe, dispatch, function (action) { | ||
return (0, _utils.asap)(function () { | ||
return dispatch(action); | ||
}); | ||
}, 0, saga.name); | ||
(0, _proc2.default)(saga(getState), sagaEmitter.subscribe, dispatch, monitor, 0, saga.name); | ||
}); | ||
@@ -900,3 +1103,5 @@ | ||
var result = next(action); // hit reducers | ||
sagaEmitter.emit(action); | ||
// filter out monitor actions to avoid endless loop | ||
// see https://github.com/yelouafi/redux-saga/issues/61 | ||
if (!action[_monitorActions.MONITOR_ACTION]) sagaEmitter.emit(action); | ||
return result; | ||
@@ -909,3 +1114,3 @@ }; | ||
/***/ }, | ||
/* 7 */ | ||
/* 8 */ | ||
/***/ function(module, exports, __webpack_require__) { | ||
@@ -924,7 +1129,7 @@ | ||
var _proc = __webpack_require__(2); | ||
var _proc = __webpack_require__(3); | ||
var _proc2 = _interopRequireDefault(_proc); | ||
var _emitter = __webpack_require__(3); | ||
var _emitter = __webpack_require__(5); | ||
@@ -972,2 +1177,99 @@ var _emitter2 = _interopRequireDefault(_emitter); | ||
/***/ }, | ||
/* 9 */ | ||
/***/ function(module, exports) { | ||
// shim for using process in browser | ||
var process = module.exports = {}; | ||
var queue = []; | ||
var draining = false; | ||
var currentQueue; | ||
var queueIndex = -1; | ||
function cleanUpNextTick() { | ||
draining = false; | ||
if (currentQueue.length) { | ||
queue = currentQueue.concat(queue); | ||
} else { | ||
queueIndex = -1; | ||
} | ||
if (queue.length) { | ||
drainQueue(); | ||
} | ||
} | ||
function drainQueue() { | ||
if (draining) { | ||
return; | ||
} | ||
var timeout = setTimeout(cleanUpNextTick); | ||
draining = true; | ||
var len = queue.length; | ||
while(len) { | ||
currentQueue = queue; | ||
queue = []; | ||
while (++queueIndex < len) { | ||
if (currentQueue) { | ||
currentQueue[queueIndex].run(); | ||
} | ||
} | ||
queueIndex = -1; | ||
len = queue.length; | ||
} | ||
currentQueue = null; | ||
draining = false; | ||
clearTimeout(timeout); | ||
} | ||
process.nextTick = function (fun) { | ||
var args = new Array(arguments.length - 1); | ||
if (arguments.length > 1) { | ||
for (var i = 1; i < arguments.length; i++) { | ||
args[i - 1] = arguments[i]; | ||
} | ||
} | ||
queue.push(new Item(fun, args)); | ||
if (queue.length === 1 && !draining) { | ||
setTimeout(drainQueue, 0); | ||
} | ||
}; | ||
// v8 likes predictible objects | ||
function Item(fun, array) { | ||
this.fun = fun; | ||
this.array = array; | ||
} | ||
Item.prototype.run = function () { | ||
this.fun.apply(null, this.array); | ||
}; | ||
process.title = 'browser'; | ||
process.browser = true; | ||
process.env = {}; | ||
process.argv = []; | ||
process.version = ''; // empty string to avoid regexp issues | ||
process.versions = {}; | ||
function noop() {} | ||
process.on = noop; | ||
process.addListener = noop; | ||
process.once = noop; | ||
process.off = noop; | ||
process.removeListener = noop; | ||
process.removeAllListeners = noop; | ||
process.emit = noop; | ||
process.binding = function (name) { | ||
throw new Error('process.binding is not supported'); | ||
}; | ||
process.cwd = function () { return '/' }; | ||
process.chdir = function (dir) { | ||
throw new Error('process.chdir is not supported'); | ||
}; | ||
process.umask = function() { return 0; }; | ||
/***/ } | ||
@@ -974,0 +1276,0 @@ /******/ ]) |
@@ -1,1 +0,1 @@ | ||
!function(e,n){"object"==typeof exports&&"object"==typeof module?module.exports=n():"function"==typeof define&&define.amd?define([],n):"object"==typeof exports?exports.ReduxSaga=n():e.ReduxSaga=n()}(this,function(){return function(e){function n(t){if(r[t])return r[t].exports;var u=r[t]={exports:{},id:t,loaded:!1};return e[t].call(u.exports,u,u.exports,n),u.loaded=!0,u.exports}var r={};return n.m=e,n.c=r,n.p="",n(0)}([function(e,n,r){"use strict";function t(e){if(e&&e.__esModule)return e;var n={};if(null!=e)for(var r in e)Object.prototype.hasOwnProperty.call(e,r)&&(n[r]=e[r]);return n.default=e,n}function u(e){return e&&e.__esModule?e:{"default":e}}Object.defineProperty(n,"__esModule",{value:!0}),n.monitorActions=n.storeIO=n.runSaga=n.as=n.cancel=n.join=n.fork=n.cps=n.apply=n.call=n.race=n.put=n.take=n.is=n.MANUAL_CANCEL=n.PARALLEL_AUTO_CANCEL=n.RACE_AUTO_CANCEL=n.SagaCancellationException=void 0;var o=r(2);Object.defineProperty(n,"SagaCancellationException",{enumerable:!0,get:function(){return o.SagaCancellationException}}),Object.defineProperty(n,"RACE_AUTO_CANCEL",{enumerable:!0,get:function(){return o.RACE_AUTO_CANCEL}}),Object.defineProperty(n,"PARALLEL_AUTO_CANCEL",{enumerable:!0,get:function(){return o.PARALLEL_AUTO_CANCEL}}),Object.defineProperty(n,"MANUAL_CANCEL",{enumerable:!0,get:function(){return o.MANUAL_CANCEL}});var a=r(1);Object.defineProperty(n,"is",{enumerable:!0,get:function(){return a.is}});var i=r(4);Object.defineProperty(n,"take",{enumerable:!0,get:function(){return i.take}}),Object.defineProperty(n,"put",{enumerable:!0,get:function(){return i.put}}),Object.defineProperty(n,"race",{enumerable:!0,get:function(){return i.race}}),Object.defineProperty(n,"call",{enumerable:!0,get:function(){return i.call}}),Object.defineProperty(n,"apply",{enumerable:!0,get:function(){return i.apply}}),Object.defineProperty(n,"cps",{enumerable:!0,get:function(){return i.cps}}),Object.defineProperty(n,"fork",{enumerable:!0,get:function(){return i.fork}}),Object.defineProperty(n,"join",{enumerable:!0,get:function(){return i.join}}),Object.defineProperty(n,"cancel",{enumerable:!0,get:function(){return i.cancel}}),Object.defineProperty(n,"as",{enumerable:!0,get:function(){return i.as}});var c=r(7);Object.defineProperty(n,"runSaga",{enumerable:!0,get:function(){return c.runSaga}}),Object.defineProperty(n,"storeIO",{enumerable:!0,get:function(){return c.storeIO}});var f=r(6),s=u(f),l=r(5),d=t(l);n.default=s.default,n.monitorActions=d},function(e,n){"use strict";function r(e,n,r){if(!n(e))throw new Error(r)}function t(e,n){var r=e.indexOf(n);r>=0&&e.splice(r,1)}function u(){var e=arguments.length<=0||void 0===arguments[0]?{}:arguments[0],n=c({},e),r=new Promise(function(e,r){n.resolve=e,n.reject=r});return n.promise=r,n}function o(e){for(var n=[],r=0;e>r;r++)n.push(u());return n}function a(){var e=arguments.length<=0||void 0===arguments[0]?0:arguments[0];return function(){return++e}}function i(e){return Promise.resolve(1).then(function(){return e()})}var c=Object.assign||function(e){for(var n=1;n<arguments.length;n++){var r=arguments[n];for(var t in r)Object.prototype.hasOwnProperty.call(r,t)&&(e[t]=r[t])}return e};Object.defineProperty(n,"__esModule",{value:!0}),n.check=r,n.remove=t,n.deferred=u,n.arrayOfDeffered=o,n.autoInc=a,n.asap=i;var f=n.TASK=Symbol("TASK"),s=(n.kTrue=function(){return!0},n.noop=function(){},n.is={undef:function(e){return null===e||void 0===e},notUndef:function(e){return null!==e&&void 0!==e},func:function(e){return"function"==typeof e},array:Array.isArray,promise:function(e){return e&&s.func(e.then)},iterator:function(e){return e&&s.func(e.next)&&s.func(e[Symbol.iterator])},"throw":function(e){return e&&s.func(e.throw)},task:function(e){return e&&e[f]}})},function(e,n,r){"use strict";function t(e){if(e&&e.__esModule)return e;var n={};if(null!=e)for(var r in e)Object.prototype.hasOwnProperty.call(e,r)&&(n[r]=e[r]);return n.default=e,n}function u(e,n,r){return n in e?Object.defineProperty(e,n,{value:r,enumerable:!0,configurable:!0,writable:!0}):e[n]=r,e}function o(e,n){if(!(e instanceof n))throw new TypeError("Cannot call a class as a function")}function a(e){function n(u,o){if(e._isRunning)try{if(o&&!x)throw u;var a=o?e.throw(u):e.next(u);if(a.done)r(a.value);else{var i=t(a.value,N);I.promise[v]=i[v],i.then(n,function(e){return n(e,!0)})}}catch(c){console.warn(w+": uncaught",c),r(c,!0)}}function r(n,r){e._isRunning=!1,r?(e._error=n,I.reject(n)):(e._result=n,I.resolve(n)),S()}function t(e,n){var r=arguments.length<=2||void 0===arguments[2]?"":arguments[2],t=g();P(s.effectTriggered(t,n,r,e));var u=void 0,l=i.is.array(e)?y(e,t):i.is.iterator(e)?a(e,T,j,P,t).done:(u=c.as.take(e))?o(u):(u=c.as.put(e))?f(u):(u=c.as.race(e))?h(u,t):(u=c.as.call(e))?E(u,t):(u=c.as.cps(e))?_(u):(u=c.as.fork(e))?R(u,t):(u=c.as.join(e))?b(u):(u=c.as.cancel(e))?O(u):Promise.resolve(e),d=(0,i.deferred)(),p=!0,m=function(e){return function(n){p&&(p=!1,e(n))}};return l.then(m(d.resolve),m(d.reject)),d.promise[v]=function(e){var n=e.type,r=e.origin;if(p){p=!1;var t=new A(n,w,r);L(l,t),d.reject(t)}},d.promise.then(function(e){return P(s.effectResolved(t,e))},function(e){return P(s.effectRejected(t,e))}),d.promise}function o(e){var n=(0,i.deferred)({match:(0,c.matcher)(e),pattern:e});k.push(n);var r=function(){return(0,i.remove)(k,n)};return n.promise.then(r,r),n.promise[v]=r,n.promise}function f(e){return(0,i.asap)(function(){return j(e)})}function E(e,n){var r=e.context,t=e.fn,u=e.args,o=t.apply(r,u);return i.is.iterator(o)?a(o,T,j,P,n,t.name).done:Promise.resolve(o)}function _(e){var n=e.context,r=e.fn,t=e.args;return new Promise(function(e,u){r.apply(n,t.concat(function(n,r){return i.is.undef(n)?e(r):u(n)}))})}function R(e,n){var r=e.context,t=e.fn,u=e.args,o=void 0,c=void 0;return o=t.apply(r,u),c=i.is.iterator(o)?o:regeneratorRuntime.mark(function f(){return regeneratorRuntime.wrap(function(e){for(;;)switch(e.prev=e.next){case 0:return e.next=2,o;case 2:return e.abrupt("return",e.sent);case 3:case"end":return e.stop()}},f,this)})(),Promise.resolve(a(c,T,j,P,n,t.name,!0))}function b(e){return e.done}function O(e){return e.done[v](new A(m,"",w)),Promise.resolve()}function y(e,n){var r=e.map(function(e){return t(e,n)}),u=Promise.all(r);return u[v]=function(e){r.forEach(function(n){return L(n,e)})},u.catch(function(){u[v](new A(d,w,w))}),u}function h(e,n){var r=[],o=Promise.race(Object.keys(e).map(function(o){var a=t(e[o],n,o);return r.push(a),a.then(function(e){return u({},o,e)},function(e){return Promise.reject(u({},o,e))})}));o[v]=function(e){r.forEach(function(n){return L(n,e)})};var a=function(){return o[v](new A(p,w,w))};return o.then(a,a),o}function C(e,n,r,t,o){var a;return a={},u(a,i.TASK,!0),u(a,"id",e),u(a,"name",n),u(a,"done",t),u(a,"forked",o),u(a,"isRunning",function(){return r._isRunning}),u(a,"getResult",function(){return r._result}),u(a,"getError",function(){return r._error}),a}function L(e,n){e[v]&&e[v](n)}var T=arguments.length<=1||void 0===arguments[1]?function(){return i.noop}:arguments[1],j=arguments.length<=2||void 0===arguments[2]?i.noop:arguments[2],P=arguments.length<=3||void 0===arguments[3]?i.noop:arguments[3],N=arguments.length<=4||void 0===arguments[4]?0:arguments[4],w=arguments.length<=5||void 0===arguments[5]?"anonymous":arguments[5];(0,i.check)(e,i.is.iterator,l);var k=[],x=i.is.throw(e),I=(0,i.deferred)(),S=T(function(e){k.forEach(function(n){n.match(e)&&n.resolve(e)})});return e._isRunning=!0,n(),C(N,w,e,I.promise)}Object.defineProperty(n,"__esModule",{value:!0}),n.SagaCancellationException=n.CANCEL=n.MANUAL_CANCEL=n.RACE_AUTO_CANCEL=n.PARALLEL_AUTO_CANCEL=n.NOT_ITERATOR_ERROR=void 0,n.default=a;var i=r(1),c=r(4),f=r(5),s=t(f),l=n.NOT_ITERATOR_ERROR="proc first argument (Saga function result) must be an iterator",d=n.PARALLEL_AUTO_CANCEL="PARALLEL_AUTO_CANCEL",p=n.RACE_AUTO_CANCEL="RACE_AUTO_CANCEL",m=n.MANUAL_CANCEL="MANUAL_CANCEL",g=(0,i.autoInc)(),v=n.CANCEL=Symbol("@@redux-saga/cancelPromise"),A=n.SagaCancellationException=function E(e,n,r){o(this,E),this.type=e,this.saga=n,this.origin=r}},function(e,n,r){"use strict";function t(){function e(e){return r.push(e),function(){return(0,u.remove)(r,e)}}function n(e){r.slice().forEach(function(n){return n(e)})}var r=[];return{subscribe:e,emit:n}}Object.defineProperty(n,"__esModule",{value:!0}),n.default=t;var u=r(1)},function(e,n,r){"use strict";function t(e,n,r){return n in e?Object.defineProperty(e,n,{value:r,enumerable:!0,configurable:!0,writable:!0}):e[n]=r,e}function u(e){return("*"===e?w.wildcard:v.is.array(e)?w.array:v.is.func(e)?w.predicate:w.default)(e)}function o(e){if(arguments.length>0&&v.is.undef(e))throw new Error(R);return N(O,v.is.undef(e)?"*":e)}function a(e){return N(y,e)}function i(e){return N(h,e)}function c(e,n){(0,v.check)(e,v.is.notUndef,A);var r=null;if(v.is.array(e)){var t=e,u=g(t,2);r=u[0],e=u[1]}else if(e.fn){var o=e;r=o.context,e=o.fn}return(0,v.check)(e,v.is.func,A),{context:r,fn:e,args:n}}function f(e){for(var n=arguments.length,r=Array(n>1?n-1:0),t=1;n>t;t++)r[t-1]=arguments[t];return N(C,c(e,r))}function s(e,n){var r=arguments.length<=2||void 0===arguments[2]?[]:arguments[2];return N(C,c({context:e,fn:n},r))}function l(e){for(var n=arguments.length,r=Array(n>1?n-1:0),t=1;n>t;t++)r[t-1]=arguments[t];return N(L,c(e,r))}function d(e){for(var n=arguments.length,r=Array(n>1?n-1:0),t=1;n>t;t++)r[t-1]=arguments[t];return N(T,c(e,r))}function p(e){if(!k(e))throw new Error(E);return N(j,e)}function m(e){if(!k(e))throw new Error(_);return N(P,e)}var g=function(){function e(e,n){var r=[],t=!0,u=!1,o=void 0;try{for(var a,i=e[Symbol.iterator]();!(t=(a=i.next()).done)&&(r.push(a.value),!n||r.length!==n);t=!0);}catch(c){u=!0,o=c}finally{try{!t&&i.return&&i.return()}finally{if(u)throw o}}return r}return function(n,r){if(Array.isArray(n))return n;if(Symbol.iterator in Object(n))return e(n,r);throw new TypeError("Invalid attempt to destructure non-iterable instance")}}();Object.defineProperty(n,"__esModule",{value:!0}),n.as=n.INVALID_PATTERN=n.CANCEL_ARG_ERROR=n.JOIN_ARG_ERROR=n.FORK_ARG_ERROR=n.CALL_FUNCTION_ARG_ERROR=void 0,n.matcher=u,n.take=o,n.put=a,n.race=i,n.call=f,n.apply=s,n.cps=l,n.fork=d,n.join=p,n.cancel=m;var v=r(1),A=n.CALL_FUNCTION_ARG_ERROR="call/cps/fork first argument must be a function, an array [context, function] or an object {context, fn}",E=(n.FORK_ARG_ERROR="fork first argument must be a generator function or an iterator",n.JOIN_ARG_ERROR="join argument must be a valid task (a result of a fork)"),_=n.CANCEL_ARG_ERROR="cancel argument must be a valid task (a result of a fork)",R=n.INVALID_PATTERN="Invalid pattern passed to `take` (HINT: check if you didn't mispell a constant)",b=Symbol("IO"),O="TAKE",y="PUT",h="RACE",C="CALL",L="CPS",T="FORK",j="JOIN",P="CANCEL",N=function(e,n){var r;return r={},t(r,b,!0),t(r,e,n),r},w={wildcard:function(){return v.kTrue},"default":function(e){return function(n){return n.type===e}},array:function(e){return function(n){return e.some(function(e){return e===n.type})}},predicate:function(e){return function(n){return e(n)}}},k=function(e){return e[v.TASK]};n.as={take:function(e){return e&&e[b]&&e[O]},put:function(e){return e&&e[b]&&e[y]},race:function(e){return e&&e[b]&&e[h]},call:function(e){return e&&e[b]&&e[C]},cps:function(e){return e&&e[b]&&e[L]},fork:function(e){return e&&e[b]&&e[T]},join:function(e){return e&&e[b]&&e[j]},cancel:function(e){return e&&e[b]&&e[P]}}},function(e,n){"use strict";function r(e,n,r,t){return{type:o,effectId:e,parentEffectId:n,label:r,effect:t}}function t(e,n){return{type:a,effectId:e,result:n}}function u(e,n){return{type:i,effectId:e,error:n}}Object.defineProperty(n,"__esModule",{value:!0}),n.effectTriggered=r,n.effectResolved=t,n.effectRejected=u;var o=n.EFFECT_TRIGGERED="EFFECT_TRIGGERED",a=n.EFFECT_RESOLVED="EFFECT_RESOLVED",i=n.EFFECT_REJECTED="EFFECT_REJECTED"},function(e,n,r){"use strict";function t(e){return e&&e.__esModule?e:{"default":e}}Object.defineProperty(n,"__esModule",{value:!0});var u=r(1),o=r(2),a=t(o),i=r(3),c=t(i);n.default=function(){for(var e=arguments.length,n=Array(e),r=0;e>r;r++)n[r]=arguments[r];return function(e){var r=e.getState,t=e.dispatch,o=(0,c.default)();return n.forEach(function(e){(0,a.default)(e(r),o.subscribe,t,function(e){return(0,u.asap)(function(){return t(e)})},0,e.name)}),function(e){return function(n){var r=e(n);return o.emit(n),r}}}}},function(e,n,r){"use strict";function t(e){return e&&e.__esModule?e:{"default":e}}function u(e){if(e[d])return e[d];var n=(0,s.default)(),r=e.dispatch;return e.dispatch=function(e){var t=r(e);return n.emit(e),t},e[d]={subscribe:n.subscribe,dispatch:e.dispatch},e[d]}function o(e,n){var r=n.subscribe,t=n.dispatch,u=arguments.length<=2||void 0===arguments[2]?a.noop:arguments[2];return(0,a.check)(e,a.is.iterator,l),(0,c.default)(e,r,t,u)}Object.defineProperty(n,"__esModule",{value:!0}),n.NOT_ITERATOR_ERROR=void 0,n.storeIO=u,n.runSaga=o;var a=r(1),i=r(2),c=t(i),f=r(3),s=t(f),l=n.NOT_ITERATOR_ERROR="runSaga must be called on an iterator",d=Symbol("IO")}])}); | ||
!function(e,n){"object"==typeof exports&&"object"==typeof module?module.exports=n():"function"==typeof define&&define.amd?define([],n):"object"==typeof exports?exports.ReduxSaga=n():e.ReduxSaga=n()}(this,function(){return function(e){function n(r){if(t[r])return t[r].exports;var u=t[r]={exports:{},id:r,loaded:!1};return e[r].call(u.exports,u,u.exports,n),u.loaded=!0,u.exports}var t={};return n.m=e,n.c=t,n.p="",n(0)}([function(e,n,t){"use strict";function r(e){if(e&&e.__esModule)return e;var n={};if(null!=e)for(var t in e)Object.prototype.hasOwnProperty.call(e,t)&&(n[t]=e[t]);return n.default=e,n}function u(e){return e&&e.__esModule?e:{"default":e}}Object.defineProperty(n,"__esModule",{value:!0}),n.monitorActions=n.storeIO=n.runSaga=n.as=n.cancel=n.join=n.fork=n.cps=n.apply=n.call=n.race=n.put=n.take=n.is=n.SagaCancellationException=n.MANUAL_CANCEL=n.PARALLEL_AUTO_CANCEL=n.RACE_AUTO_CANCEL=n.CANCEL=void 0;var o=t(3);Object.defineProperty(n,"CANCEL",{enumerable:!0,get:function(){return o.CANCEL}}),Object.defineProperty(n,"RACE_AUTO_CANCEL",{enumerable:!0,get:function(){return o.RACE_AUTO_CANCEL}}),Object.defineProperty(n,"PARALLEL_AUTO_CANCEL",{enumerable:!0,get:function(){return o.PARALLEL_AUTO_CANCEL}}),Object.defineProperty(n,"MANUAL_CANCEL",{enumerable:!0,get:function(){return o.MANUAL_CANCEL}});var a=t(1);Object.defineProperty(n,"is",{enumerable:!0,get:function(){return a.is}});var i=t(6);Object.defineProperty(n,"take",{enumerable:!0,get:function(){return i.take}}),Object.defineProperty(n,"put",{enumerable:!0,get:function(){return i.put}}),Object.defineProperty(n,"race",{enumerable:!0,get:function(){return i.race}}),Object.defineProperty(n,"call",{enumerable:!0,get:function(){return i.call}}),Object.defineProperty(n,"apply",{enumerable:!0,get:function(){return i.apply}}),Object.defineProperty(n,"cps",{enumerable:!0,get:function(){return i.cps}}),Object.defineProperty(n,"fork",{enumerable:!0,get:function(){return i.fork}}),Object.defineProperty(n,"join",{enumerable:!0,get:function(){return i.join}}),Object.defineProperty(n,"cancel",{enumerable:!0,get:function(){return i.cancel}}),Object.defineProperty(n,"as",{enumerable:!0,get:function(){return i.as}});var c=t(8);Object.defineProperty(n,"runSaga",{enumerable:!0,get:function(){return c.runSaga}}),Object.defineProperty(n,"storeIO",{enumerable:!0,get:function(){return c.storeIO}});var f=t(7),s=u(f),l=t(4),d=u(l),p=t(2),g=r(p);n.default=s.default;n.SagaCancellationException=d.default;n.monitorActions=g},function(e,n,t){(function(e){"use strict";function t(e,n,t){if(!n(e))throw new Error(t)}function r(e,n){var t=e.indexOf(n);t>=0&&e.splice(t,1)}function u(){var e=arguments.length<=0||void 0===arguments[0]?{}:arguments[0],n=c({},e),t=new Promise(function(e,t){n.resolve=e,n.reject=t});return n.promise=t,n}function o(e){for(var n=[],t=0;e>t;t++)n.push(u());return n}function a(){var e=arguments.length<=0||void 0===arguments[0]?0:arguments[0];return function(){return++e}}function i(e){return Promise.resolve(1).then(function(){return e()})}var c=Object.assign||function(e){for(var n=1;n<arguments.length;n++){var t=arguments[n];for(var r in t)Object.prototype.hasOwnProperty.call(t,r)&&(e[r]=t[r])}return e};Object.defineProperty(n,"__esModule",{value:!0}),n.check=t,n.remove=r,n.deferred=u,n.arrayOfDeffered=o,n.autoInc=a,n.asap=i;var f=n.TASK=Symbol("TASK"),s=(n.kTrue=function(){return!0},n.noop=function(){},n.isDev="undefined"!=typeof e&&e.env&&!1,n.is={undef:function(e){return null===e||void 0===e},notUndef:function(e){return null!==e&&void 0!==e},func:function(e){return"function"==typeof e},array:Array.isArray,promise:function(e){return e&&s.func(e.then)},iterator:function(e){return e&&s.func(e.next)&&s.func(e[Symbol.iterator])},"throw":function(e){return e&&s.func(e.throw)},task:function(e){return e&&e[f]}})}).call(n,t(9))},function(e,n){"use strict";function t(e,n,t){return n in e?Object.defineProperty(e,n,{value:t,enumerable:!0,configurable:!0,writable:!0}):e[n]=t,e}function r(e,n,r,u){var o;return o={},t(o,a,!0),t(o,"type",i),t(o,"effectId",e),t(o,"parentEffectId",n),t(o,"label",r),t(o,"effect",u),o}function u(e,n){var r;return r={},t(r,a,!0),t(r,"type",c),t(r,"effectId",e),t(r,"result",n),r}function o(e,n){var r;return r={},t(r,a,!0),t(r,"type",f),t(r,"effectId",e),t(r,"error",n),r}Object.defineProperty(n,"__esModule",{value:!0}),n.effectTriggered=r,n.effectResolved=u,n.effectRejected=o;var a=n.MONITOR_ACTION="MONITOR_ACTION",i=n.EFFECT_TRIGGERED="EFFECT_TRIGGERED",c=n.EFFECT_RESOLVED="EFFECT_RESOLVED",f=n.EFFECT_REJECTED="EFFECT_REJECTED"},function(e,n,t){"use strict";function r(e){return e&&e.__esModule?e:{"default":e}}function u(e){if(e&&e.__esModule)return e;var n={};if(null!=e)for(var t in e)Object.prototype.hasOwnProperty.call(e,t)&&(n[t]=e[t]);return n.default=e,n}function o(e,n,t){return n in e?Object.defineProperty(e,n,{value:t,enumerable:!0,configurable:!0,writable:!0}):e[n]=t,e}function a(e){function n(u,o){if(!e._isRunning)throw new Error("Trying to resume an already finished generator");try{if(u&&!x)throw u;var a=u?e.throw(u):e.next(o);a.done?t(a.value):r(a.value,P,"",n)}catch(u){i.isDev&&console.warn(I+": uncaught",u),t(u,!0)}}function t(n,t){e._isRunning=!1,t?(e._error=n,U.reject(n)):(e._result=n,U.resolve(n)),S()}function r(e,n){function t(e,n){d||(d=!0,o.cancel=i.noop,w(e?s.effectRejected(a,e):s.effectResolved(a,n)),o(e,n))}var r=arguments.length<=2||void 0===arguments[2]?"":arguments[2],o=arguments[3],a=E();w(s.effectTriggered(a,n,r,e));var d=void 0;t.cancel=i.noop,o.cancel=function(e){if(!d){d=!0;try{t.cancel(e)}catch(n){}t.cancel=i.noop,o(e),w(s.effectRejected(a,e))}};var p=void 0;return i.is.promise(e)?u(e,t):i.is.iterator(e)?f(e,a,I,t):i.is.array(e)?C(e,a,t):i.is.notUndef(p=c.as.take(e))?l(p,t):i.is.notUndef(p=c.as.put(e))?_(p,t):i.is.notUndef(p=c.as.race(e))?L(p,a,t):i.is.notUndef(p=c.as.call(e))?y(p,a,t):i.is.notUndef(p=c.as.cps(e))?h(p,t):i.is.notUndef(p=c.as.fork(e))?O(p,a,t):i.is.notUndef(p=c.as.join(e))?b(p,t):i.is.notUndef(p=c.as.cancel(e))?R(p,t):t(null,e)}function u(e,n){var t=e[g];"function"==typeof t&&(n.cancel=t),e.then(function(e){return n(null,e)},function(e){return n(e)})}function f(e,n,t,r){u(a(e,N,j,w,n,t).done,r)}function l(e,n){var t={match:(0,c.matcher)(e),pattern:e,resolve:function(e){return n(null,e)}};k.push(t),n.cancel=function(){return(0,i.remove)(k,t)}}function _(e,n){(0,i.asap)(function(){return n(null,j(e))})}function y(e,n,t){var r=e.context,o=e.fn,a=e.args,c=o.apply(r,a);return i.is.promise(c)?u(c,t):i.is.iterator(c)?f(c,n,o.name,t):t(null,c)}function h(e,n){var t=e.context,r=e.fn,u=e.args;r.apply(t,u.concat(n))}function O(e,n,t){var r=e.context,u=e.fn,o=e.args,c=void 0,f=void 0;c=u.apply(r,o),f=i.is.iterator(c)?c:regeneratorRuntime.mark(function s(){return regeneratorRuntime.wrap(function(e){for(;;)switch(e.prev=e.next){case 0:return e.next=2,c;case 2:return e.abrupt("return",e.sent);case 3:case"end":return e.stop()}},s,this)})(),t(null,a(f,N,j,w,n,u.name,!0))}function b(e,n){u(e.done,n)}function R(e,n){e.done[g](new d.default(A,I,I)),n()}function C(e,n,t){var u=0,o=void 0,a=Array(e.length),c=e.map(function(e,n){var r=function(e,r){if(!o)if(e){try{t.cancel(new d.default(v,I,I))}catch(e){}t(e)}else a[n]=r,u++,u===a.length&&(o=!0,t(null,a))};return r.cancel=i.noop,r});t.cancel=function(e){o||(o=!0,c.forEach(function(n){return n.cancel(e)}))},e.forEach(function(e,t){return r(e,n,t,c[t])})}function L(e,n,t){var u=void 0,a=Object.keys(e),c={};a.forEach(function(e){var n=function(n,r){if(!u)if(n){try{t.cancel(new d.default(m,I,I))}catch(n){}t(o({},e,n))}else{try{t.cancel(new d.default(m,I,I))}catch(n){}u=!0,t(null,o({},e,r))}};n.cancel=i.noop,c[e]=n}),t.cancel=function(e){u||(u=!0,a.forEach(function(n){return c[n].cancel(e)}))},a.forEach(function(t){return r(e[t],n,t,c[t])})}function T(e,n,t,r,u){var a;return a={},o(a,i.TASK,!0),o(a,"id",e),o(a,"name",n),o(a,"done",r),o(a,"forked",u),o(a,"isRunning",function(){return t._isRunning}),o(a,"getResult",function(){return t._result}),o(a,"getError",function(){return t._error}),a}var N=arguments.length<=1||void 0===arguments[1]?function(){return i.noop}:arguments[1],j=arguments.length<=2||void 0===arguments[2]?i.noop:arguments[2],w=arguments.length<=3||void 0===arguments[3]?i.noop:arguments[3],P=arguments.length<=4||void 0===arguments[4]?0:arguments[4],I=arguments.length<=5||void 0===arguments[5]?"anonymous":arguments[5];(0,i.check)(e,i.is.iterator,p);var k=[],x=i.is.throw(e),U=(0,i.deferred)(),S=N(function(e){for(var n=0;n<k.length;n++){var t=k[n];t.match(e)&&(k=[],t.resolve(e))}});n.cancel=i.noop;var M=T(P,I,e,U.promise);return M.done[g]=function(e){var t=e.type,r=e.origin;n.cancel(new d.default(t,I,r))},e._isRunning=!0,n(),M}Object.defineProperty(n,"__esModule",{value:!0}),n.MANUAL_CANCEL=n.RACE_AUTO_CANCEL=n.PARALLEL_AUTO_CANCEL=n.CANCEL=n.NOT_ITERATOR_ERROR=void 0,n.default=a;var i=t(1),c=t(6),f=t(2),s=u(f),l=t(4),d=r(l),p=n.NOT_ITERATOR_ERROR="proc first argument (Saga function result) must be an iterator",g=n.CANCEL=Symbol("@@redux-saga/cancelPromise"),v=n.PARALLEL_AUTO_CANCEL="PARALLEL_AUTO_CANCEL",m=n.RACE_AUTO_CANCEL="RACE_AUTO_CANCEL",A=n.MANUAL_CANCEL="MANUAL_CANCEL",E=(0,i.autoInc)()},function(e,n){"use strict";function t(e,n,t){var r="SagaCancellationException; type: "+e+", saga: "+n+", origin: "+t;this.name="SagaCancellationException",this.message=r,this.type=e,this.saga=n,this.origin=t,this.stack=(new Error).stack}Object.defineProperty(n,"__esModule",{value:!0}),n.default=t,t.prototype=Object.create(Error.prototype),t.prototype.constructor=t},function(e,n,t){"use strict";function r(){function e(e){return t.push(e),function(){return(0,u.remove)(t,e)}}function n(e){t.slice().forEach(function(n){return n(e)})}var t=[];return{subscribe:e,emit:n}}Object.defineProperty(n,"__esModule",{value:!0}),n.default=r;var u=t(1)},function(e,n,t){"use strict";function r(e,n,t){return n in e?Object.defineProperty(e,n,{value:t,enumerable:!0,configurable:!0,writable:!0}):e[n]=t,e}function u(e){return("*"===e?P.wildcard:m.is.array(e)?P.array:m.is.func(e)?P.predicate:P.default)(e)}function o(e){if(arguments.length>0&&m.is.undef(e))throw new Error(y);return w(O,m.is.undef(e)?"*":e)}function a(e){return w(b,e)}function i(e){return w(R,e)}function c(e,n){(0,m.check)(e,m.is.notUndef,A);var t=null;if(m.is.array(e)){var r=e,u=v(r,2);t=u[0],e=u[1]}else if(e.fn){var o=e;t=o.context,e=o.fn}return(0,m.check)(e,m.is.func,A),{context:t,fn:e,args:n}}function f(e){for(var n=arguments.length,t=Array(n>1?n-1:0),r=1;n>r;r++)t[r-1]=arguments[r];return w(C,c(e,t))}function s(e,n){var t=arguments.length<=2||void 0===arguments[2]?[]:arguments[2];return w(C,c({context:e,fn:n},t))}function l(e){for(var n=arguments.length,t=Array(n>1?n-1:0),r=1;n>r;r++)t[r-1]=arguments[r];return w(L,c(e,t))}function d(e){for(var n=arguments.length,t=Array(n>1?n-1:0),r=1;n>r;r++)t[r-1]=arguments[r];return w(T,c(e,t))}function p(e){if(!I(e))throw new Error(E);return w(N,e)}function g(e){if(!I(e))throw new Error(_);return w(j,e)}var v=function(){function e(e,n){var t=[],r=!0,u=!1,o=void 0;try{for(var a,i=e[Symbol.iterator]();!(r=(a=i.next()).done)&&(t.push(a.value),!n||t.length!==n);r=!0);}catch(c){u=!0,o=c}finally{try{!r&&i.return&&i.return()}finally{if(u)throw o}}return t}return function(n,t){if(Array.isArray(n))return n;if(Symbol.iterator in Object(n))return e(n,t);throw new TypeError("Invalid attempt to destructure non-iterable instance")}}();Object.defineProperty(n,"__esModule",{value:!0}),n.as=n.INVALID_PATTERN=n.CANCEL_ARG_ERROR=n.JOIN_ARG_ERROR=n.FORK_ARG_ERROR=n.CALL_FUNCTION_ARG_ERROR=void 0,n.matcher=u,n.take=o,n.put=a,n.race=i,n.call=f,n.apply=s,n.cps=l,n.fork=d,n.join=p,n.cancel=g;var m=t(1),A=n.CALL_FUNCTION_ARG_ERROR="call/cps/fork first argument must be a function, an array [context, function] or an object {context, fn}",E=(n.FORK_ARG_ERROR="fork first argument must be a generator function or an iterator",n.JOIN_ARG_ERROR="join argument must be a valid task (a result of a fork)"),_=n.CANCEL_ARG_ERROR="cancel argument must be a valid task (a result of a fork)",y=n.INVALID_PATTERN="Invalid pattern passed to `take` (HINT: check if you didn't mispell a constant)",h=Symbol("IO"),O="TAKE",b="PUT",R="RACE",C="CALL",L="CPS",T="FORK",N="JOIN",j="CANCEL",w=function(e,n){var t;return t={},r(t,h,!0),r(t,e,n),t},P={wildcard:function(){return m.kTrue},"default":function(e){return function(n){return n.type===e}},array:function(e){return function(n){return e.some(function(e){return e===n.type})}},predicate:function(e){return function(n){return e(n)}}},I=function(e){return e[m.TASK]};n.as={take:function(e){return e&&e[h]&&e[O]},put:function(e){return e&&e[h]&&e[b]},race:function(e){return e&&e[h]&&e[R]},call:function(e){return e&&e[h]&&e[C]},cps:function(e){return e&&e[h]&&e[L]},fork:function(e){return e&&e[h]&&e[T]},join:function(e){return e&&e[h]&&e[N]},cancel:function(e){return e&&e[h]&&e[j]}}},function(e,n,t){"use strict";function r(e){return e&&e.__esModule?e:{"default":e}}Object.defineProperty(n,"__esModule",{value:!0});var u=t(1),o=t(3),a=r(o),i=t(5),c=r(i),f=t(2);n.default=function(){for(var e=arguments.length,n=Array(e),t=0;e>t;t++)n[t]=arguments[t];return function(e){var t=e.getState,r=e.dispatch,o=(0,c.default)(),i=u.isDev?function(e){return(0,u.asap)(function(){return r(e)})}:void 0;return n.forEach(function(e){(0,a.default)(e(t),o.subscribe,r,i,0,e.name)}),function(e){return function(n){var t=e(n);return n[f.MONITOR_ACTION]||o.emit(n),t}}}}},function(e,n,t){"use strict";function r(e){return e&&e.__esModule?e:{"default":e}}function u(e){if(e[d])return e[d];var n=(0,s.default)(),t=e.dispatch;return e.dispatch=function(e){var r=t(e);return n.emit(e),r},e[d]={subscribe:n.subscribe,dispatch:e.dispatch},e[d]}function o(e,n){var t=n.subscribe,r=n.dispatch,u=arguments.length<=2||void 0===arguments[2]?a.noop:arguments[2];return(0,a.check)(e,a.is.iterator,l),(0,c.default)(e,t,r,u)}Object.defineProperty(n,"__esModule",{value:!0}),n.NOT_ITERATOR_ERROR=void 0,n.storeIO=u,n.runSaga=o;var a=t(1),i=t(3),c=r(i),f=t(5),s=r(f),l=n.NOT_ITERATOR_ERROR="runSaga must be called on an iterator",d=Symbol("IO")},function(e,n){function t(){f=!1,a.length?c=a.concat(c):s=-1,c.length&&r()}function r(){if(!f){var e=setTimeout(t);f=!0;for(var n=c.length;n;){for(a=c,c=[];++s<n;)a&&a[s].run();s=-1,n=c.length}a=null,f=!1,clearTimeout(e)}}function u(e,n){this.fun=e,this.array=n}function o(){}var a,i=e.exports={},c=[],f=!1,s=-1;i.nextTick=function(e){var n=new Array(arguments.length-1);if(arguments.length>1)for(var t=1;t<arguments.length;t++)n[t-1]=arguments[t];c.push(new u(e,n)),1!==c.length||f||setTimeout(r,0)},u.prototype.run=function(){this.fun.apply(null,this.array)},i.title="browser",i.browser=!0,i.env={},i.argv=[],i.version="",i.versions={},i.on=o,i.addListener=o,i.once=o,i.off=o,i.removeListener=o,i.removeAllListeners=o,i.emit=o,i.binding=function(e){throw new Error("process.binding is not supported")},i.cwd=function(){return"/"},i.chdir=function(e){throw new Error("process.chdir is not supported")},i.umask=function(){return 0}}])}); |
@@ -6,10 +6,10 @@ 'use strict'; | ||
}); | ||
exports.monitorActions = exports.storeIO = exports.runSaga = exports.as = exports.cancel = exports.join = exports.fork = exports.cps = exports.apply = exports.call = exports.race = exports.put = exports.take = exports.is = exports.MANUAL_CANCEL = exports.PARALLEL_AUTO_CANCEL = exports.RACE_AUTO_CANCEL = exports.SagaCancellationException = undefined; | ||
exports.monitorActions = exports.storeIO = exports.runSaga = exports.as = exports.cancel = exports.join = exports.fork = exports.cps = exports.apply = exports.call = exports.race = exports.put = exports.take = exports.is = exports.SagaCancellationException = exports.MANUAL_CANCEL = exports.PARALLEL_AUTO_CANCEL = exports.RACE_AUTO_CANCEL = exports.CANCEL = undefined; | ||
var _proc = require('./proc'); | ||
Object.defineProperty(exports, 'SagaCancellationException', { | ||
Object.defineProperty(exports, 'CANCEL', { | ||
enumerable: true, | ||
get: function get() { | ||
return _proc.SagaCancellationException; | ||
return _proc.CANCEL; | ||
} | ||
@@ -127,2 +127,6 @@ }); | ||
var _SagaCancellationException2 = require('./SagaCancellationException'); | ||
var _SagaCancellationException3 = _interopRequireDefault(_SagaCancellationException2); | ||
var _monitorActions = require('./monitorActions'); | ||
@@ -137,2 +141,4 @@ | ||
exports.default = _middleware2.default; | ||
var SagaCancellationException = exports.SagaCancellationException = _SagaCancellationException3.default; | ||
exports.monitorActions = monitorActions; |
@@ -17,2 +17,4 @@ 'use strict'; | ||
var _monitorActions = require('./monitorActions'); | ||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } | ||
@@ -30,9 +32,10 @@ | ||
var sagaEmitter = (0, _emitter2.default)(); | ||
var monitor = _utils.isDev ? function (action) { | ||
return (0, _utils.asap)(function () { | ||
return dispatch(action); | ||
}); | ||
} : undefined; | ||
sagas.forEach(function (saga) { | ||
(0, _proc2.default)(saga(getState), sagaEmitter.subscribe, dispatch, function (action) { | ||
return (0, _utils.asap)(function () { | ||
return dispatch(action); | ||
}); | ||
}, 0, saga.name); | ||
(0, _proc2.default)(saga(getState), sagaEmitter.subscribe, dispatch, monitor, 0, saga.name); | ||
}); | ||
@@ -43,3 +46,5 @@ | ||
var result = next(action); // hit reducers | ||
sagaEmitter.emit(action); | ||
// filter out monitor actions to avoid endless loop | ||
// see https://github.com/yelouafi/redux-saga/issues/61 | ||
if (!action[_monitorActions.MONITOR_ACTION]) sagaEmitter.emit(action); | ||
return result; | ||
@@ -46,0 +51,0 @@ }; |
@@ -9,2 +9,6 @@ 'use strict'; | ||
exports.effectRejected = effectRejected; | ||
function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } | ||
var MONITOR_ACTION = exports.MONITOR_ACTION = 'MONITOR_ACTION'; | ||
var EFFECT_TRIGGERED = exports.EFFECT_TRIGGERED = 'EFFECT_TRIGGERED'; | ||
@@ -15,20 +19,17 @@ var EFFECT_RESOLVED = exports.EFFECT_RESOLVED = 'EFFECT_RESOLVED'; | ||
function effectTriggered(effectId, parentEffectId, label, effect) { | ||
return { | ||
type: EFFECT_TRIGGERED, | ||
effectId: effectId, parentEffectId: parentEffectId, label: label, effect: effect | ||
}; | ||
var _ref; | ||
return _ref = {}, _defineProperty(_ref, MONITOR_ACTION, true), _defineProperty(_ref, 'type', EFFECT_TRIGGERED), _defineProperty(_ref, 'effectId', effectId), _defineProperty(_ref, 'parentEffectId', parentEffectId), _defineProperty(_ref, 'label', label), _defineProperty(_ref, 'effect', effect), _ref; | ||
} | ||
function effectResolved(effectId, result) { | ||
return { | ||
type: EFFECT_RESOLVED, | ||
effectId: effectId, result: result | ||
}; | ||
var _ref2; | ||
return _ref2 = {}, _defineProperty(_ref2, MONITOR_ACTION, true), _defineProperty(_ref2, 'type', EFFECT_RESOLVED), _defineProperty(_ref2, 'effectId', effectId), _defineProperty(_ref2, 'result', result), _ref2; | ||
} | ||
function effectRejected(effectId, error) { | ||
return { | ||
type: EFFECT_REJECTED, | ||
effectId: effectId, error: error | ||
}; | ||
var _ref3; | ||
return _ref3 = {}, _defineProperty(_ref3, MONITOR_ACTION, true), _defineProperty(_ref3, 'type', EFFECT_REJECTED), _defineProperty(_ref3, 'effectId', effectId), _defineProperty(_ref3, 'error', error), _ref3; | ||
} |
389
lib/proc.js
@@ -6,3 +6,3 @@ 'use strict'; | ||
}); | ||
exports.SagaCancellationException = exports.CANCEL = exports.MANUAL_CANCEL = exports.RACE_AUTO_CANCEL = exports.PARALLEL_AUTO_CANCEL = exports.NOT_ITERATOR_ERROR = undefined; | ||
exports.MANUAL_CANCEL = exports.RACE_AUTO_CANCEL = exports.PARALLEL_AUTO_CANCEL = exports.CANCEL = exports.NOT_ITERATOR_ERROR = undefined; | ||
exports.default = proc; | ||
@@ -18,2 +18,8 @@ | ||
var _SagaCancellationException = require('./SagaCancellationException'); | ||
var _SagaCancellationException2 = _interopRequireDefault(_SagaCancellationException); | ||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } | ||
function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } } | ||
@@ -23,5 +29,4 @@ | ||
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } | ||
var NOT_ITERATOR_ERROR = exports.NOT_ITERATOR_ERROR = 'proc first argument (Saga function result) must be an iterator'; | ||
var CANCEL = exports.CANCEL = Symbol('@@redux-saga/cancelPromise'); | ||
var PARALLEL_AUTO_CANCEL = exports.PARALLEL_AUTO_CANCEL = 'PARALLEL_AUTO_CANCEL'; | ||
@@ -33,12 +38,2 @@ var RACE_AUTO_CANCEL = exports.RACE_AUTO_CANCEL = 'RACE_AUTO_CANCEL'; | ||
var CANCEL = exports.CANCEL = Symbol('@@redux-saga/cancelPromise'); | ||
var SagaCancellationException = exports.SagaCancellationException = function SagaCancellationException(type, saga, origin) { | ||
_classCallCheck(this, SagaCancellationException); | ||
this.type = type; | ||
this.saga = saga; | ||
this.origin = origin; | ||
}; | ||
function proc(iterator) { | ||
@@ -55,28 +50,72 @@ var subscribe = arguments.length <= 1 || arguments[1] === undefined ? function () { | ||
// tracks the current `take` effects | ||
var deferredInputs = []; | ||
var canThrow = _utils.is.throw(iterator); | ||
// Promise to be resolved/rejected when this generator terminates (or throws) | ||
var deferredEnd = (0, _utils.deferred)(); | ||
// subscribe to input events, this will resolve the current `take` effects | ||
var unsubscribe = subscribe(function (input) { | ||
deferredInputs.forEach(function (def) { | ||
if (def.match(input)) def.resolve(input); | ||
}); | ||
for (var i = 0; i < deferredInputs.length; i++) { | ||
var def = deferredInputs[i]; | ||
if (def.match(input)) { | ||
// cancel all deferredInputs; parallel takes are disallowed | ||
// and in concurrent takes, first wins | ||
deferredInputs = []; | ||
def.resolve(input); | ||
} | ||
} | ||
}); | ||
/** | ||
cancel : (SagaCancellationException) -> () | ||
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 | ||
**/ | ||
var task = newTask(parentEffectId, name, iterator, deferredEnd.promise); | ||
/** | ||
this maybe called by a parent generator to trigger/propagate cancellation | ||
W'll simply cancel the current effect, which will reject that effect | ||
The rejection will throw the injected SagaCancellationException into the flow | ||
of this generator | ||
**/ | ||
task.done[CANCEL] = function (_ref) { | ||
var type = _ref.type; | ||
var origin = _ref.origin; | ||
next.cancel(new _SagaCancellationException2.default(type, name, origin)); | ||
}; | ||
// tracks the running status | ||
iterator._isRunning = true; | ||
// kicks up the generator | ||
next(); | ||
return newTask(parentEffectId, name, iterator, deferredEnd.promise); | ||
// then return the task descriptor to the caller | ||
return task; | ||
function next(arg, isError) { | ||
if (!iterator._isRunning) return; | ||
/** | ||
This is the generator driver | ||
It's a recursive aysnc/continuation function which calls itself | ||
until the generator terminates or throws | ||
**/ | ||
function next(error, arg) { | ||
// Preventive measure. If we endup here, then there is really something wrong | ||
if (!iterator._isRunning) throw new Error('Trying to resume an already finished generator'); | ||
try { | ||
if (isError && !canThrow) throw arg; | ||
var result = isError ? iterator.throw(arg) : iterator.next(arg); | ||
if (error && !canThrow) throw error; | ||
// calling iterator.throw on a generator that doesnt defined a correponding try/Catch | ||
var result = error ? iterator.throw(error) : iterator.next(arg); | ||
if (!result.done) { | ||
var currentEffect = runEffect(result.value, parentEffectId); | ||
deferredEnd.promise[CANCEL] = currentEffect[CANCEL]; | ||
currentEffect.then(next, function (err) { | ||
return next(err, true); | ||
}); | ||
runEffect(result.value, parentEffectId, '', next); | ||
} else { | ||
@@ -87,3 +126,5 @@ end(result.value); | ||
/*eslint-disable no-console*/ | ||
console.warn(name + ': uncaught', error); | ||
if (_utils.isDev) { | ||
console.warn(name + ': uncaught', error); | ||
} | ||
end(error, true); | ||
@@ -107,2 +148,3 @@ } | ||
var label = arguments.length <= 2 || arguments[2] === undefined ? '' : arguments[2]; | ||
var cb = arguments[3]; | ||
@@ -112,54 +154,111 @@ var effectId = nextEffectId(); | ||
var data = undefined; | ||
var promise = _utils.is.array(effect) ? runParallelEffect(effect, effectId) : _utils.is.iterator(effect) ? proc(effect, subscribe, dispatch, monitor, effectId).done : (data = _io.as.take(effect)) ? runTakeEffect(data) : (data = _io.as.put(effect)) ? runPutEffect(data) : (data = _io.as.race(effect)) ? runRaceEffect(data, effectId) : (data = _io.as.call(effect)) ? runCallEffect(data, effectId) : (data = _io.as.cps(effect)) ? runCPSEffect(data) : (data = _io.as.fork(effect)) ? runForkEffect(data, effectId) : (data = _io.as.join(effect)) ? runJoinEffect(data) : (data = _io.as.cancel(effect)) ? runCancelEffect(data) : /* resolve anything else */Promise.resolve(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 = undefined; | ||
var def = (0, _utils.deferred)(); | ||
var isRunning = true; | ||
var completeWith = function completeWith(fn) { | ||
return function (outcome) { | ||
if (isRunning) { | ||
isRunning = false; | ||
fn(outcome); | ||
} | ||
}; | ||
}; | ||
promise.then(completeWith(def.resolve), completeWith(def.reject)); | ||
def.promise[CANCEL] = function (_ref) { | ||
var type = _ref.type; | ||
var origin = _ref.origin; | ||
// Completion callback passed to the appropriate effect runner | ||
function currCb(err, res) { | ||
if (effectSettled) return; | ||
if (isRunning) { | ||
isRunning = false; | ||
var error = new SagaCancellationException(type, name, origin); | ||
cancelPromise(promise, error); | ||
def.reject(error); | ||
effectSettled = true; | ||
cb.cancel = _utils.noop; // defensive measure | ||
err ? monitor(monitorActions.effectRejected(effectId, err)) : monitor(monitorActions.effectResolved(effectId, res)); | ||
cb(err, res); | ||
} | ||
// tracks down the current cancel | ||
currCb.cancel = _utils.noop; | ||
// setup cancellation logic on the parent cb | ||
cb.cancel = function (cancelError) { | ||
// prevents cancelling an already completed effect | ||
if (effectSettled) return; | ||
effectSettled = true; | ||
/** | ||
propagates cancel downward | ||
catch uncaught cancellations errors, | ||
because w'll throw our own cancellation error inside this generator | ||
**/ | ||
try { | ||
currCb.cancel(cancelError); | ||
} catch (err) { | ||
void 0; | ||
} | ||
currCb.cancel = _utils.noop; // defensive measure | ||
/** | ||
triggers/propagates the cancellation error | ||
**/ | ||
cb(cancelError); | ||
monitor(monitorActions.effectRejected(effectId, cancelError)); | ||
}; | ||
def.promise.then(function (result) { | ||
return monitor(monitorActions.effectResolved(effectId, result)); | ||
/** | ||
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 | ||
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 | ||
**/ | ||
var data = undefined; | ||
return( | ||
// Non declarative effect | ||
_utils.is.promise(effect) ? resolvePromise(effect, currCb) : _utils.is.iterator(effect) ? resolveIterator(effect, effectId, name, currCb) | ||
// declarative effects | ||
: _utils.is.array(effect) ? runParallelEffect(effect, effectId, currCb) : _utils.is.notUndef(data = _io.as.take(effect)) ? runTakeEffect(data, currCb) : _utils.is.notUndef(data = _io.as.put(effect)) ? runPutEffect(data, currCb) : _utils.is.notUndef(data = _io.as.race(effect)) ? runRaceEffect(data, effectId, currCb) : _utils.is.notUndef(data = _io.as.call(effect)) ? runCallEffect(data, effectId, currCb) : _utils.is.notUndef(data = _io.as.cps(effect)) ? runCPSEffect(data, currCb) : _utils.is.notUndef(data = _io.as.fork(effect)) ? runForkEffect(data, effectId, currCb) : _utils.is.notUndef(data = _io.as.join(effect)) ? runJoinEffect(data, currCb) : _utils.is.notUndef(data = _io.as.cancel(effect)) ? runCancelEffect(data, currCb) : /* anything else returned as is */currCb(null, effect) | ||
); | ||
} | ||
function resolvePromise(promise, cb) { | ||
var cancelPromise = promise[CANCEL]; | ||
if (typeof cancelPromise === 'function') { | ||
cb.cancel = cancelPromise; | ||
} | ||
promise.then(function (result) { | ||
return cb(null, result); | ||
}, function (error) { | ||
return monitor(monitorActions.effectRejected(effectId, error)); | ||
return cb(error); | ||
}); | ||
return def.promise; | ||
} | ||
function runTakeEffect(pattern) { | ||
var def = (0, _utils.deferred)({ match: (0, _io.matcher)(pattern), pattern: pattern }); | ||
function resolveIterator(iterator, effectId, name, cb) { | ||
resolvePromise(proc(iterator, subscribe, dispatch, monitor, effectId, name).done, cb); | ||
} | ||
function runTakeEffect(pattern, cb) { | ||
var def = { | ||
match: (0, _io.matcher)(pattern), | ||
pattern: pattern, | ||
resolve: function resolve(input) { | ||
return cb(null, input); | ||
} | ||
}; | ||
deferredInputs.push(def); | ||
var done = function done() { | ||
// cancellation logic for take effect | ||
cb.cancel = function () { | ||
return (0, _utils.remove)(deferredInputs, def); | ||
}; | ||
def.promise.then(done, done); | ||
def.promise[CANCEL] = done; | ||
return def.promise; | ||
} | ||
function runPutEffect(action) { | ||
return (0, _utils.asap)(function () { | ||
return dispatch(action); | ||
function runPutEffect(action, cb) { | ||
// TODO check why synchronously nested dispatches aren't forwarded to the store | ||
// For now, this workaround allows the dispatch to occur on the next microtask | ||
// But could have side effect on a sync interleaved take/dispatch flow (ARGGHHHHHHH) | ||
(0, _utils.asap)(function () { | ||
return cb(null, dispatch(action)); | ||
}); | ||
// Put effects are non cancellables | ||
} | ||
function runCallEffect(_ref2, effectId) { | ||
function runCallEffect(_ref2, effectId, cb) { | ||
var context = _ref2.context; | ||
@@ -170,6 +269,6 @@ var fn = _ref2.fn; | ||
var result = fn.apply(context, args); | ||
return !_utils.is.iterator(result) ? Promise.resolve(result) : proc(result, subscribe, dispatch, monitor, effectId, fn.name).done; | ||
return _utils.is.promise(result) ? resolvePromise(result, cb) : _utils.is.iterator(result) ? resolveIterator(result, effectId, fn.name, cb) : cb(null, result); | ||
} | ||
function runCPSEffect(_ref3) { | ||
function runCPSEffect(_ref3, cb) { | ||
var context = _ref3.context; | ||
@@ -179,10 +278,8 @@ var fn = _ref3.fn; | ||
return new Promise(function (resolve, reject) { | ||
fn.apply(context, args.concat(function (err, res) { | ||
return _utils.is.undef(err) ? resolve(res) : reject(err); | ||
})); | ||
}); | ||
// CPS (ie node style functions) can define their own cancellation logic | ||
// by setting cancel field on the cb | ||
fn.apply(context, args.concat(cb)); | ||
} | ||
function runForkEffect(_ref4, effectId) { | ||
function runForkEffect(_ref4, effectId, cb) { | ||
var context = _ref4.context; | ||
@@ -226,71 +323,131 @@ var fn = _ref4.fn; | ||
return Promise.resolve(proc(_iterator, subscribe, dispatch, monitor, effectId, fn.name, true)); | ||
cb(null, proc(_iterator, subscribe, dispatch, monitor, effectId, fn.name, true)); | ||
// Fork effects are non cancellables | ||
} | ||
function runJoinEffect(task) { | ||
return task.done; | ||
function runJoinEffect(task, cb) { | ||
resolvePromise(task.done, cb); | ||
} | ||
function runCancelEffect(task) { | ||
task.done[CANCEL](new SagaCancellationException(MANUAL_CANCEL, '', name)); | ||
return Promise.resolve(); | ||
function runCancelEffect(task, cb) { | ||
// cancel the given task | ||
// uncaught cancellations errors bubbles upward | ||
task.done[CANCEL](new _SagaCancellationException2.default(MANUAL_CANCEL, name, name)); | ||
cb(); | ||
// cancel effects are non cancellables | ||
} | ||
function runParallelEffect(effects, effectId) { | ||
var promises = effects.map(function (eff) { | ||
return runEffect(eff, effectId); | ||
// Reimplementing Promise.all. We're in 2016 | ||
function runParallelEffect(effects, effectId, cb) { | ||
var completedCount = 0; | ||
var completed = undefined; | ||
var results = Array(effects.length); | ||
var childCbs = effects.map(function (eff, idx) { | ||
var chCbAtIdx = function chCbAtIdx(err, res) { | ||
// Either we've been cancelled, or an error aborted the whole effect | ||
if (completed) return; | ||
// one of the effects failed | ||
if (err) { | ||
// cancel all other effects | ||
// This is an AUTO_CANCEL (not triggered by a manual cancel) | ||
// Catch uncaught cancellation errors, because w'll only throw the actual | ||
// rejection error (err) inside this generator | ||
try { | ||
cb.cancel(new _SagaCancellationException2.default(PARALLEL_AUTO_CANCEL, name, name)); | ||
} catch (err) { | ||
void 0; | ||
} | ||
cb(err); | ||
} else { | ||
results[idx] = res; | ||
completedCount++; | ||
if (completedCount === results.length) { | ||
completed = true; | ||
cb(null, results); | ||
} | ||
} | ||
}; | ||
chCbAtIdx.cancel = _utils.noop; | ||
return chCbAtIdx; | ||
}); | ||
var ret = Promise.all(promises); | ||
ret[CANCEL] = function (error) { | ||
promises.forEach(function (p) { | ||
return cancelPromise(p, error); | ||
}); | ||
// This is different, a cancellation coming from upward | ||
// either a MANUAL_CANCEL or a parent AUTO_CANCEL | ||
// No need to catch, will be swallowed by the caller | ||
cb.cancel = function (cancelError) { | ||
// prevents unnecessary cancellation | ||
if (!completed) { | ||
completed = true; | ||
childCbs.forEach(function (chCb) { | ||
return chCb.cancel(cancelError); | ||
}); | ||
} | ||
}; | ||
ret.catch(function () { | ||
ret[CANCEL](new SagaCancellationException(PARALLEL_AUTO_CANCEL, name, name)); | ||
effects.forEach(function (eff, idx) { | ||
return runEffect(eff, effectId, idx, childCbs[idx]); | ||
}); | ||
return ret; | ||
} | ||
function runRaceEffect(effects, effectId) { | ||
var promises = []; | ||
var retP = Promise.race(Object.keys(effects).map(function (key) { | ||
var promise = runEffect(effects[key], effectId, key); | ||
promises.push(promise); | ||
return promise.then(function (result) { | ||
return _defineProperty({}, key, result); | ||
}, function (error) { | ||
return Promise.reject(_defineProperty({}, key, error)); | ||
}); | ||
})); | ||
// And yet; Promise.race | ||
function runRaceEffect(effects, effectId, cb) { | ||
var completed = undefined; | ||
var keys = Object.keys(effects); | ||
var childCbs = {}; | ||
retP[CANCEL] = function (error) { | ||
promises.forEach(function (p) { | ||
return cancelPromise(p, error); | ||
}); | ||
}; | ||
keys.forEach(function (key) { | ||
var chCbAtKey = function chCbAtKey(err, res) { | ||
// Either we've been cancelled, or an error aborted the whole effect | ||
if (completed) return; | ||
var done = function done() { | ||
return retP[CANCEL](new SagaCancellationException(RACE_AUTO_CANCEL, name, name)); | ||
if (err) { | ||
// Race Auto cancellation | ||
try { | ||
cb.cancel(new _SagaCancellationException2.default(RACE_AUTO_CANCEL, name, name)); | ||
} catch (err) { | ||
void 0; | ||
} | ||
cb(_defineProperty({}, key, err)); | ||
} else { | ||
try { | ||
cb.cancel(new _SagaCancellationException2.default(RACE_AUTO_CANCEL, name, name)); | ||
} catch (err) { | ||
void 0; | ||
} | ||
completed = true; | ||
cb(null, _defineProperty({}, key, res)); | ||
} | ||
}; | ||
chCbAtKey.cancel = _utils.noop; | ||
childCbs[key] = chCbAtKey; | ||
}); | ||
cb.cancel = function (cancelError) { | ||
// prevents unnecessary cancellation | ||
if (!completed) { | ||
completed = true; | ||
keys.forEach(function (key) { | ||
return childCbs[key].cancel(cancelError); | ||
}); | ||
} | ||
}; | ||
retP.then(done, done); | ||
return retP; | ||
keys.forEach(function (key) { | ||
return runEffect(effects[key], effectId, key, childCbs[key]); | ||
}); | ||
} | ||
function newTask(id, name, iterator, done, forked) { | ||
var _ref6; | ||
var _ref5; | ||
return _ref6 = {}, _defineProperty(_ref6, _utils.TASK, true), _defineProperty(_ref6, 'id', id), _defineProperty(_ref6, 'name', name), _defineProperty(_ref6, 'done', done), _defineProperty(_ref6, 'forked', forked), _defineProperty(_ref6, 'isRunning', function isRunning() { | ||
return _ref5 = {}, _defineProperty(_ref5, _utils.TASK, true), _defineProperty(_ref5, 'id', id), _defineProperty(_ref5, 'name', name), _defineProperty(_ref5, 'done', done), _defineProperty(_ref5, 'forked', forked), _defineProperty(_ref5, 'isRunning', function isRunning() { | ||
return iterator._isRunning; | ||
}), _defineProperty(_ref6, 'getResult', function getResult() { | ||
}), _defineProperty(_ref5, 'getResult', function getResult() { | ||
return iterator._result; | ||
}), _defineProperty(_ref6, 'getError', function getError() { | ||
}), _defineProperty(_ref5, 'getError', function getError() { | ||
return iterator._error; | ||
}), _ref6; | ||
}), _ref5; | ||
} | ||
function cancelPromise(promise, error) { | ||
if (promise[CANCEL]) promise[CANCEL](error); | ||
} | ||
} |
@@ -20,2 +20,4 @@ 'use strict'; | ||
var isDev = exports.isDev = typeof process !== 'undefined' && process.env && process.env.NODE_ENV === 'development'; | ||
function check(value, predicate, error) { | ||
@@ -22,0 +24,0 @@ if (!predicate(value)) throw new Error(error); |
{ | ||
"name": "redux-saga", | ||
"version": "0.5.0", | ||
"version": "0.6.0", | ||
"description": "Saga middleware for Redux to handle Side Effects", | ||
@@ -5,0 +5,0 @@ "main": "lib/index.js", |
@@ -7,3 +7,3 @@ # redux-saga | ||
这里意外着程序逻辑会存在两个位置。 | ||
这里意味着程序逻辑会存在两个位置。 | ||
@@ -20,3 +20,3 @@ - Reducers 负责处理action的state转换 | ||
- 一个目的是抽象出 **Effect** (影响): 等待一个action,触发State更新 (使用分配action给store), 调用远程服务,这些都是不同像是的Effect。Saga使用常见的控制流程(if, while, for, try/catch)去组和这些Effect。 | ||
- 一个目的是抽象出 **Effect** (影响): 等待一个action,触发State更新 (使用分配action给store), 调用远程服务,这些都是不同形式的Effect。Saga使用常见的控制流程(if, while, for, try/catch)去组和这些Effect。 | ||
@@ -80,3 +80,3 @@ - Saga本身就是Effect。它可以通过选择器和其他Effect组合。它也可以被内部的其他Saga调用,提供丰富功能的子程序和 | ||
插入redux-sago到中间件管道 | ||
插入redux-saga到中间件管道 | ||
@@ -102,9 +102,9 @@ ```javascript | ||
典型的, 实际的中间件处理一些Effect构成,它们会被Action创建者触发。举个例子, | ||
典型的, 实际的中间件处理一些Effect构成,它们会被Action Creators触发。举个例子, | ||
redux-thunk 通过调用 带 `(getState, dispatch)` 带参数的thunks来处理 *thunks* 。 | ||
redux-promise 通过调度Promise的返回值来处理Promise。redux-gen通过调用所有的迭代Action到store来处理生成器。所有这些中间件的共通是 '请求每个action'模式。当action发生的时候,他们会被一次又一次的调用。也就是说, 每次触发 *根 action*的时候, 它们都会被 *检索*。 | ||
Sagas 工作方式是不一样的,他们不是被Action创建者雇佣,但是和你的应用一起被启动,并且选择监视哪些action。它们类似于守护任务,运行在后台,并且选择他们自己的逻辑进展。在上面的例子,`incrementAsync` 使用 `yield take(...)` *拉* `INCREMENT_ASYNC` action。这是一个 *阻塞调用*, 这意味着Saga 如果没有找到匹配的action将不会推进。 | ||
Sagas 工作方式是不一样的,他们不是在Action Creators内被触发,而是随你的应用一起被启动,并且选择监视哪些action。它们类似于守护任务,运行在后台,并且选择他们自己的逻辑进展。在上面的例子,`incrementAsync` 使用 `yield take(...)` *等待* `INCREMENT_ASYNC` action。这是一个 *阻塞调用*, 这意味着Saga 如果没有找到匹配的action将不会继续执行。 | ||
上文,我们使用`take(INCREMENT_ASYNC)`的形式,它的意思是我们等待一个action它的type是`INCREMENT_ASYNC`。实际上,确切的签名是 `take(PATTERN)`, 它的模式可以下面的一种: | ||
上文,我们使用`take(INCREMENT_ASYNC)`的形式,意思是我们等待一个type是`INCREMENT_ASYNC`的action。实际上,确切的签名是 `take(PATTERN)`, 它的模式可以下面的一种: | ||
@@ -122,13 +122,13 @@ | ||
收到查询action之后,Saga触发器调用`delay(1000)`,在我们的例子中返回一个约定(Promise),这个将在1秒后解决。这是一个阻塞调用,所以Saga会等待一秒后再继续执行。 | ||
接收到需要的action之后,Saga触发器调用`delay(1000)`,在我们的例子中返回一个约定(Promise),这个将在1秒后解决。这是一个阻塞调用,所以Saga会等待一秒后再继续执行。 | ||
延迟之后,,Saga使用 `put(action)`函数调度 `INCREMENT_COUNTER` action。与此同时,Sago会等待调度结果。如果返回普通值,Saga立刻唤醒 *immediately*,但是如果返回值是一个约定,Sago会等待这个约定完成(或失败)。 | ||
延迟之后,Saga使用 `put(action)`函数调度 `INCREMENT_COUNTER` action。与此同时,Saga会等待调度结果。如果返回普通值,Saga立刻唤醒 *immediately*,但是如果返回值是一个Promise,Saga会等待这个Promise完成(或失败)。 | ||
#A common abstraction: Effect | ||
概况的说,等待一个未知的action,等待像`yield delay(1000)`这样的未知的函数调用结果,或者等待一个调度的结果,这些都是相同的概念。在所有情况下,我们迭代某些形式的影响。Saga所做的,实际上就是把所有这些影响组合在一起,去实现期望的控制流。最简单的是通过把迭代一个挨着一个的执行,去顺序迭代影响。你也可以说使用常见的控制操作(if,while,for)去实现更复杂的控制流。或者你可以使用提供的影响组合去表达并发 (yield race) 和 平行 (yield [...])。你也可以迭代调用其他Saga,允许强大的常规或者子程序模式。 | ||
一般来说,等待一个未知的action,等待像`yield delay(1000)`这样的未知的函数调用结果,或者等待一个调度的结果,这些都是相同的概念。在所有情况下,我们迭代某些形式的Effect。Saga所做的,实际上就是把所有这些Effect组合在一起,去实现期望的控制流。最简单的是一个接着一个的顺序执行yield来迭代Effect。你也可以使用常见的控制操作(if,while,for)去实现更复杂的控制流。或者你可以使用提供的Effect组合去表达并发 (yield race) 和 平行 (yield [...])。你也可以迭代调用其他Saga,允许强大的常规或者子程序模式。 | ||
举例来说,`incrementAsync` 使用了无限循环 `while(true)`,它意味着这将会在整个应用程序的生命周期都会存在。 | ||
你也可以创建Saga只持续一段时间。举个例子,下面的Saga,等待3个`INCREMENT_COUNTER` actions, 触发一个`showCongratulation()` action,然后结束. | ||
你也可以创建只持续一段时间的Saga。举个例子,下面的Saga,等待3个`INCREMENT_COUNTER` actions, 触发一个`showCongratulation()` action,然后结束. | ||
@@ -147,3 +147,3 @@ ```javascript | ||
Sagas 生成器可以生成多种形式的影响。最简单的方式是生成一个约定。 | ||
Sagas 生成器可以生成多种形式的Effect。最简单的方式是生成一个Promise。 | ||
@@ -162,3 +162,3 @@ ```javascript | ||
上面的例子,`fetch('/products')` 返回一个约定并且会被Get请求解决。所以这个获取响应会被立刻执行。简单并且顺畅,但是... | ||
上面的例子,`fetch('/products')` 返回一个Promise并且会被Get请求解决。所以这个获取响应会被立刻执行。简单并且顺畅,但是... | ||
@@ -217,3 +217,3 @@ 假设我们想要测试上面的生成器。 | ||
`call` 和 `apply`是非常适合函数返回约定结果。还有一个函数 `cps` 可以被使用到处理Node风格的函数(举例: `fn(...args, callback)` where `callback` | ||
`call` 和 `apply`是非常适合函数返回Promise结果。还有一个函数 `cps` 可以被使用到处理Node风格的函数(举例: `fn(...args, callback)` where `callback` | ||
是`(error, result) => ()`的形式)。 举个例子 | ||
@@ -238,3 +238,3 @@ | ||
你可以在Generator内部使用简单的try/catch语法捕捉异常。在下面的例子中,Saga捕捉 `api.buyProducts` 调用的错误(也就是一个被拒绝的约定) | ||
你可以在Generator内部使用简单的try/catch语法捕捉异常。在下面的例子中,Saga捕捉 `api.buyProducts` 调用的错误(也就是一个被拒绝的Promise) | ||
@@ -280,3 +280,3 @@ ```javascript | ||
`yield`声明是非常棒。它以一个简单并且线形的方式表示异步控制流程。但是我们也需要做一些并行的事情。你不能简单的如下写 | ||
`yield`声明非常棒。它以一个简单并且线形的方式表示异步控制流程。但是我们也需要做一些并行的事情。你不能简单的如下写 | ||
@@ -289,3 +289,3 @@ ```javascript | ||
因为第二个Effect将不会等到第一个执行结束后再执行,我们必须如下: | ||
因为第二个Effect将等到第一个执行结束后再执行,我们必须改成如下形式: | ||
@@ -304,3 +304,3 @@ ```javascript | ||
有些时候我们开始并行多次任务,但是我们不想等待,我们只需要去得到 *胜利者*:第一个成功运行(或者被拒绝)。`race`函数提供了一种方式去触发多个effect的竞赛。 | ||
有些时候我们开始并行多次任务,但是我们不想等待,我们只想得到 *胜利者*:第一个成功运行(或者被拒绝)。`race`函数提供了一种方式去触发多个effect的竞赛。 | ||
@@ -357,3 +357,3 @@ 下面的例子展示Saga触发一个远程的获取请求和强迫这个请求1秒过期。 | ||
当使用`yield*`提供一个习惯的方式组合Saga。这个方式有一些局限: | ||
当使用`yield*`提供的方式组合Saga时,有一些局限: | ||
@@ -390,3 +390,3 @@ - 你可能想分别测试嵌入的生成器。在测试代码中,这导致一些重复代码这和重复执行的开销是一样的。我们不想执行嵌入的生成器,但是只想确保它被分发正确的参数。 | ||
实际上, Sagas比迭代其他effect没有什么不同(未来action, timeouts ...)。这意味着你可以通过所有的其他方式,使用Effect协调器组合这些Saga。 | ||
实际上,Sagas相比迭代其他effect没有什么不同(未来action, timeouts ...)。这意味着你可以通过所有的其他方式,使用Effect协调器组合这些Saga。 | ||
@@ -429,4 +429,4 @@ 举个例子你也可能想用户必须在规定时间内完成游戏。 | ||
`watchFetch` 生成器将会等待到`yield call(fetchApi, '/posts')` 运行结束。想像 | ||
`FETCH_POSTS` action 被 `刷新`按钮触发。如果你的应用每次获取禁用这个按钮(不存在并发获取),这里将不会有问题,因为我们自导没有`FETCH_POSTS`action会发生直到我们得到`fetchApi`调用的响应。 | ||
`watchFetch` 生成器将会等待到`yield call(fetchApi, '/posts')` 运行结束。设想 | ||
`FETCH_POSTS` action 被 `刷新`按钮触发。如果我们的应用每次获取禁用这个按钮(不存在并发获取),这里将不会有问题,因为我们知道没有`FETCH_POSTS`action会发生直到我们得到`fetchApi`调用的响应。 | ||
@@ -515,3 +515,3 @@ 但是当应用程序允许用户点击`刷新`按钮而不需要等待当前请求完成,什么事情会发生? | ||
<td>task.done</td> | ||
<td>一个约定 | ||
<td>一个Promise | ||
<ul> | ||
@@ -601,3 +601,3 @@ <li>任务完成并返回值 with task's return value</li> | ||
>记住`yield cancel(task)`不会等待取消任务这个操作完成,这一点非常重要。(换句话说去执行它的异常处理是取消这个操作完成的时候)。cancel的作用有点像fork。当cancel开始它就返回值。 | ||
>一旦取消,任务需要尽快完成它的清理逻辑。在有些场合,清理逻辑可以包含一些异步操作,取消任务是一个单独的进程,并且这里没有办法重新进入主控制流程(除非通过Redux store调度actiond。然而这会导致复杂的,很难理解的控制流程。 所以最好是尽快结束任务). | ||
>一旦取消,任务需要尽快完成它的清理逻辑。在有些场合,清理逻辑可以包含一些异步操作,取消任务是一个单独的进程,并且这里没有办法重新进入主控制流程(除非通过Redux store调度action。然而这会导致复杂的,很难理解的控制流程。 所以最好是尽快结束任务). | ||
@@ -653,3 +653,3 @@ ##Automatic cancellation | ||
- `dispatch(action) => result`: 用于完成 `put` effect。每次运行`yield put(action)`,`dispatch`会和`action`一起被调用,`dispatch`的返回值被用于完成`put` effect。约定结果自动完成或者取消。 | ||
- `dispatch(action) => result`: 用于完成 `put` effect。每次运行`yield put(action)`,`dispatch`会和`action`一起被调用,`dispatch`的返回值被用于完成`put` effect。Promise结果自动完成或者取消。 | ||
@@ -656,0 +656,0 @@ - `monitor(sagaAction)` (可选): 是被用于调用所有Saga关联事件的回调。在中间件的版本,所有action都调度到Redux Store。详细查看[sagaMonitor example] |
@@ -17,3 +17,3 @@ # redux-saga | ||
> As you'll in the rest of this README. Generators, while they seem lower level than ES7 async | ||
> As you'll see in the rest of this README. Generators, while they seem lower level than ES7 async | ||
functions, allow some features like declarative effects, cancellation. Which are harder, if Not | ||
@@ -55,2 +55,3 @@ impossible, to implement with simple async functions. | ||
#Getting started | ||
@@ -131,3 +132,3 @@ | ||
- If PATTERN is a function, the action is matched if PATTERN(action) is true (e.g. `take(action => action.entities)` | ||
will match all actions having a (truthy) `entities`field. | ||
will match all actions having a (truthy) `entities`field.) | ||
@@ -137,3 +138,3 @@ - If PATTERN is a string, the action is matched if action.type === PATTERN (as used above `take(INCREMENT_ASYNC)` | ||
- If PATTERN is an array, action.type is matched against all items in the array (e.g. `take([INCREMENT, DECREMENT])` will | ||
match either actions of type `INCREMENT` or `DECREMENT`. | ||
match either actions of type `INCREMENT` or `DECREMENT`.) | ||
@@ -541,3 +542,3 @@ #Dispatching actions to the store | ||
`fork` accepts function/generator calls as well as simple effects | ||
`fork`, just like `call`, accepts function/generator calls. | ||
@@ -638,3 +639,3 @@ ```javascript | ||
// user clicked stop. cancel the background task | ||
// this will throw a SagaCancellationException into task | ||
// this will throw a SagaCancellationException into the forked bgSync task | ||
yield cancel(bgSyncTask) | ||
@@ -647,5 +648,5 @@ } | ||
inside the currently running task. In the above example, the exception is caught by | ||
`bgSync`. Otherwise, it will propagate up to `main`. And it if `main` doesn't handle it | ||
then it will bubble up the call chain, just as normal JavaScript errors bubble up the | ||
call chain of synchronous functions. | ||
`bgSync`. **Note that uncaught SagaCancellationException are not bubbled upward**. In the | ||
above example, if `bgSync` doesn't catch the cancellation error, the error will not propagate | ||
to `main` (because `main` has already moved on). | ||
@@ -681,3 +682,5 @@ Cancelling a running task will also cancel the current effect where the task is blocked | ||
then another `SagaCancellationException` will be thrown inside `subtask`. If `subtask` | ||
omits to handle the cancellation exception, it will propagate up to `main`. | ||
omits to handle the cancellation exception, a warning message is printed to the console to | ||
warn the developer (the message is only printed if there is a `process.env.NODE_ENV` variable | ||
set and it's set to `'development'`). | ||
@@ -711,5 +714,2 @@ The main purpose of the cancellation exception is to allow cancelled tasks to perform any | ||
Unlike in manual cancellations, unhandled cancellation exceptions are not propagated to the actual | ||
saga running the race/parallel effect. Nevertheless, a warning is logged into the console in case | ||
a cancelled task omitted to handle a cancellation exception. | ||
@@ -774,9 +774,2 @@ #Dynamically starting Sagas with runSaga | ||
Pre-requisites | ||
- browserify | ||
You can also build the examples manually, and open `index.html` at the root of each example | ||
directory to run. | ||
``` | ||
@@ -793,8 +786,4 @@ git clone https://github.com/yelouafi/redux-saga.git | ||
``` | ||
// run with live-reload server | ||
npm run counter | ||
// manual build | ||
npm run build-counter | ||
// test sample for the generator | ||
@@ -806,8 +795,4 @@ npm run test-counter | ||
``` | ||
// run with live-reload server | ||
npm run shop | ||
// manual build | ||
npm run build-shop | ||
// test sample for the generator | ||
@@ -819,8 +804,4 @@ npm run test-shop | ||
``` | ||
// run with live-reload server | ||
npm run async | ||
// manual build | ||
npm run build-async | ||
//sorry, no tests yet | ||
@@ -827,0 +808,0 @@ ``` |
@@ -5,3 +5,3 @@ import middleware from './middleware' | ||
export { | ||
SagaCancellationException, | ||
CANCEL, | ||
RACE_AUTO_CANCEL, | ||
@@ -12,2 +12,6 @@ PARALLEL_AUTO_CANCEL, | ||
import _SagaCancellationException from './SagaCancellationException' | ||
export const SagaCancellationException = _SagaCancellationException | ||
export { is } from './utils' | ||
@@ -14,0 +18,0 @@ export { take, put, race, call, apply, cps, fork, join, cancel, as } from './io' |
@@ -1,8 +0,11 @@ | ||
import { asap } from './utils' | ||
import { asap, isDev } from './utils' | ||
import proc from './proc' | ||
import emitter from './emitter' | ||
import { MONITOR_ACTION } from './monitorActions' | ||
export default (...sagas) => ({getState, dispatch}) => { | ||
const sagaEmitter = emitter() | ||
const monitor = isDev ? action => asap(() => dispatch(action)) : undefined | ||
@@ -14,3 +17,3 @@ sagas.forEach( saga => { | ||
dispatch, | ||
action => asap(() => dispatch(action)), | ||
monitor, | ||
0, | ||
@@ -23,3 +26,6 @@ saga.name | ||
const result = next(action) // hit reducers | ||
sagaEmitter.emit(action) | ||
// filter out monitor actions to avoid endless loop | ||
// see https://github.com/yelouafi/redux-saga/issues/61 | ||
if(!action[MONITOR_ACTION]) | ||
sagaEmitter.emit(action) | ||
return result; | ||
@@ -29,2 +35,3 @@ } | ||
} |
@@ -1,2 +0,2 @@ | ||
export const MONITOR_ACTION = 'MONITOR_ACTION' | ||
export const EFFECT_TRIGGERED = 'EFFECT_TRIGGERED' | ||
@@ -8,2 +8,3 @@ export const EFFECT_RESOLVED = 'EFFECT_RESOLVED' | ||
return { | ||
[MONITOR_ACTION]: true, | ||
type: EFFECT_TRIGGERED, | ||
@@ -16,2 +17,3 @@ effectId, parentEffectId, label, effect | ||
return { | ||
[MONITOR_ACTION]: true, | ||
type: EFFECT_RESOLVED, | ||
@@ -24,2 +26,3 @@ effectId, result | ||
return { | ||
[MONITOR_ACTION]: true, | ||
type: EFFECT_REJECTED, | ||
@@ -26,0 +29,0 @@ effectId, error |
409
src/proc.js
@@ -1,7 +0,9 @@ | ||
import { noop, is, check, remove, deferred, autoInc, asap, TASK } from './utils' | ||
import { noop, is, isDev, check, remove, deferred, autoInc, asap, TASK } from './utils' | ||
import { as, matcher } from './io' | ||
import * as monitorActions from './monitorActions' | ||
import SagaCancellationException from './SagaCancellationException' | ||
export const NOT_ITERATOR_ERROR = 'proc first argument (Saga function result) must be an iterator' | ||
export const CANCEL = Symbol('@@redux-saga/cancelPromise') | ||
export const PARALLEL_AUTO_CANCEL = 'PARALLEL_AUTO_CANCEL' | ||
@@ -13,12 +15,2 @@ export const RACE_AUTO_CANCEL = 'RACE_AUTO_CANCEL' | ||
export const CANCEL = Symbol('@@redux-saga/cancelPromise') | ||
export class SagaCancellationException { | ||
constructor(type, saga, origin) { | ||
this.type = type | ||
this.saga = saga | ||
this.origin = origin | ||
} | ||
} | ||
export default function proc( | ||
@@ -35,31 +27,76 @@ iterator, | ||
const deferredInputs = [] | ||
// tracks the current `take` effects | ||
let deferredInputs = [] | ||
const canThrow = is.throw(iterator) | ||
// Promise to be resolved/rejected when this generator terminates (or throws) | ||
const deferredEnd = deferred() | ||
// subscribe to input events, this will resolve the current `take` effects | ||
const unsubscribe = subscribe(input => { | ||
deferredInputs.forEach( def => { | ||
if(def.match(input)) | ||
def.resolve(input) | ||
}) | ||
}) | ||
for (let i = 0; i < deferredInputs.length; i++) { | ||
const def = deferredInputs[i] | ||
if(def.match(input)) { | ||
// cancel all deferredInputs; parallel takes are disallowed | ||
// and in concurrent takes, first wins | ||
deferredInputs = [] | ||
def.resolve(input) | ||
} | ||
} | ||
}) | ||
iterator._isRunning = true | ||
next() | ||
/** | ||
cancel : (SagaCancellationException) -> () | ||
return newTask( | ||
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 = noop | ||
/** | ||
Creates a new task descriptor for this generator | ||
**/ | ||
const task = newTask( | ||
parentEffectId, name, iterator, deferredEnd.promise | ||
) | ||
function next(arg, isError) { | ||
/** | ||
this maybe called by a parent generator to trigger/propagate cancellation | ||
W'll simply cancel the current effect, which will reject that effect | ||
The rejection will throw the injected SagaCancellationException into the flow | ||
of this generator | ||
**/ | ||
task.done[CANCEL] = ({type, origin}) => { | ||
next.cancel( | ||
new SagaCancellationException(type, name, origin) | ||
) | ||
} | ||
// 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 aysnc/continuation function which calls itself | ||
until the generator terminates or throws | ||
**/ | ||
function next(error, arg) { | ||
// Preventive measure. If we endup here, then there is really something wrong | ||
if(!iterator._isRunning) | ||
return | ||
throw new Error('Trying to resume an already finished generator') | ||
try { | ||
if(isError && !canThrow) | ||
throw arg | ||
const result = isError ? iterator.throw(arg) : iterator.next(arg) | ||
if(error && !canThrow) | ||
throw error | ||
// calling iterator.throw on a generator that doesnt defined a correponding try/Catch | ||
const result = error ? iterator.throw(error) : iterator.next(arg) | ||
if(!result.done) { | ||
const currentEffect = runEffect(result.value, parentEffectId) | ||
deferredEnd.promise[CANCEL] = currentEffect[CANCEL] | ||
currentEffect.then(next, err => next(err, true)) | ||
runEffect(result.value, parentEffectId, '', next) | ||
} else { | ||
@@ -70,3 +107,5 @@ end(result.value) | ||
/*eslint-disable no-console*/ | ||
console.warn(`${name}: uncaught`, error ) | ||
if(isDev) { | ||
console.warn(`${name}: uncaught`, error ) | ||
} | ||
end(error, true) | ||
@@ -88,78 +127,143 @@ } | ||
function runEffect(effect, parentEffectId, label = '') { | ||
function runEffect(effect, parentEffectId, label = '', cb) { | ||
const effectId = nextEffectId() | ||
monitor( monitorActions.effectTriggered(effectId, parentEffectId, label, effect) ) | ||
let data | ||
const promise = ( | ||
is.array(effect) ? runParallelEffect(effect, effectId) | ||
: is.iterator(effect) ? proc(effect, subscribe, dispatch, monitor, effectId).done | ||
/** | ||
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 | ||
**/ | ||
let effectSettled | ||
: (data = as.take(effect)) ? runTakeEffect(data) | ||
: (data = as.put(effect)) ? runPutEffect(data) | ||
: (data = as.race(effect)) ? runRaceEffect(data, effectId) | ||
: (data = as.call(effect)) ? runCallEffect(data, effectId) | ||
: (data = as.cps(effect)) ? runCPSEffect(data) | ||
: (data = as.fork(effect)) ? runForkEffect(data, effectId) | ||
: (data = as.join(effect)) ? runJoinEffect(data) | ||
: (data = as.cancel(effect)) ? runCancelEffect(data) | ||
// Completion callback passed to the appropriate effect runner | ||
function currCb(err, res) { | ||
if(effectSettled) | ||
return | ||
: /* resolve anything else */ Promise.resolve(effect) | ||
) | ||
effectSettled = true | ||
cb.cancel = noop // defensive measure | ||
err ? | ||
monitor( monitorActions.effectRejected(effectId, err) ) | ||
: monitor( monitorActions.effectResolved(effectId, res) ) | ||
const def = deferred() | ||
let isRunning = true | ||
const completeWith = fn => outcome => { | ||
if(isRunning) { | ||
isRunning = false | ||
fn(outcome) | ||
} | ||
cb(err, res) | ||
} | ||
promise.then(completeWith(def.resolve), completeWith(def.reject)) | ||
def.promise[CANCEL] = ({type, origin}) => { | ||
if(isRunning) { | ||
isRunning = false | ||
const error = new SagaCancellationException(type, name, origin) | ||
cancelPromise(promise, error) | ||
def.reject(error) | ||
} | ||
// tracks down the current cancel | ||
currCb.cancel = noop | ||
// setup cancellation logic on the parent cb | ||
cb.cancel = (cancelError) => { | ||
// prevents cancelling an already completed effect | ||
if(effectSettled) | ||
return | ||
effectSettled = true | ||
/** | ||
propagates cancel downward | ||
catch uncaught cancellations errors, | ||
because w'll throw our own cancellation error inside this generator | ||
**/ | ||
try { currCb.cancel(cancelError) } catch(err) { void(0); } | ||
currCb.cancel = noop // defensive measure | ||
/** | ||
triggers/propagates the cancellation error | ||
**/ | ||
cb(cancelError) | ||
monitor( monitorActions.effectRejected(effectId, cancelError) ) | ||
} | ||
def.promise.then( | ||
result => monitor( monitorActions.effectResolved(effectId, result) ), | ||
error => monitor( monitorActions.effectRejected(effectId, error) ) | ||
/** | ||
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 | ||
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 | ||
**/ | ||
let data | ||
return ( | ||
// Non declarative effect | ||
is.promise(effect) ? resolvePromise(effect, currCb) | ||
: is.iterator(effect) ? resolveIterator(effect, effectId, name, currCb) | ||
// declarative effects | ||
: is.array(effect) ? runParallelEffect(effect, effectId, currCb) | ||
: (is.notUndef(data = as.take(effect))) ? runTakeEffect(data, currCb) | ||
: (is.notUndef(data = as.put(effect))) ? runPutEffect(data, currCb) | ||
: (is.notUndef(data = as.race(effect))) ? runRaceEffect(data, effectId, currCb) | ||
: (is.notUndef(data = as.call(effect))) ? runCallEffect(data, effectId, currCb) | ||
: (is.notUndef(data = as.cps(effect))) ? runCPSEffect(data, currCb) | ||
: (is.notUndef(data = as.fork(effect))) ? runForkEffect(data, effectId, currCb) | ||
: (is.notUndef(data = as.join(effect))) ? runJoinEffect(data, currCb) | ||
: (is.notUndef(data = as.cancel(effect))) ? runCancelEffect(data, currCb) | ||
: /* anything else returned as is */ currCb(null, effect) | ||
) | ||
return def.promise | ||
} | ||
function runTakeEffect(pattern) { | ||
const def = deferred({ match : matcher(pattern), pattern }) | ||
function resolvePromise(promise, cb) { | ||
const cancelPromise = promise[CANCEL] | ||
if(typeof cancelPromise === 'function') { | ||
cb.cancel = cancelPromise | ||
} | ||
promise.then( | ||
result => cb(null, result), | ||
error => cb(error) | ||
) | ||
} | ||
function resolveIterator(iterator, effectId, name, cb) { | ||
resolvePromise( | ||
proc(iterator, subscribe, dispatch, monitor, effectId, name).done, | ||
cb | ||
) | ||
} | ||
function runTakeEffect(pattern, cb) { | ||
const def = { | ||
match : matcher(pattern), | ||
pattern, | ||
resolve: input => cb(null, input) | ||
} | ||
deferredInputs.push(def) | ||
const done = () => remove(deferredInputs, def) | ||
def.promise.then(done, done) | ||
def.promise[CANCEL] = done | ||
return def.promise | ||
// cancellation logic for take effect | ||
cb.cancel = () => remove(deferredInputs, def) | ||
} | ||
function runPutEffect(action) { | ||
return asap(() => dispatch(action) ) | ||
function runPutEffect(action, cb) { | ||
// TODO check why synchronously nested dispatches aren't forwarded to the store | ||
// For now, this workaround allows the dispatch to occur on the next microtask | ||
// But could have side effect on a sync interleaved take/dispatch flow (ARGGHHHHHHH) | ||
asap(() => cb(null, dispatch(action))) | ||
// Put effects are non cancellables | ||
} | ||
function runCallEffect({context, fn, args}, effectId) { | ||
function runCallEffect({context, fn, args}, effectId, cb) { | ||
const result = fn.apply(context, args) | ||
return !is.iterator(result) | ||
? Promise.resolve(result) | ||
: proc(result, subscribe, dispatch, monitor, effectId, fn.name).done | ||
return ( | ||
is.promise(result) ? resolvePromise(result, cb) | ||
: is.iterator(result) ? resolveIterator(result, effectId, fn.name, cb) | ||
: cb(null, result) | ||
) | ||
} | ||
function runCPSEffect({context, fn, args}) { | ||
return new Promise((resolve, reject) => { | ||
fn.apply(context, args.concat( | ||
(err, res) => is.undef(err) ? resolve(res) : reject(err) | ||
)) | ||
}) | ||
function runCPSEffect({context, fn, args}, cb) { | ||
// CPS (ie node style functions) can define their own cancellation logic | ||
// by setting cancel field on the cb | ||
fn.apply(context, args.concat(cb)) | ||
} | ||
function runForkEffect({context, fn, args}, effectId) { | ||
function runForkEffect({context, fn, args}, effectId, cb) { | ||
let result, _iterator | ||
@@ -183,56 +287,117 @@ | ||
return Promise.resolve( | ||
cb( | ||
null, | ||
proc(_iterator, subscribe, dispatch, monitor, effectId, fn.name, true) | ||
) | ||
// Fork effects are non cancellables | ||
} | ||
function runJoinEffect(task) { | ||
return task.done | ||
function runJoinEffect(task, cb) { | ||
resolvePromise(task.done, cb) | ||
} | ||
function runCancelEffect(task) { | ||
function runCancelEffect(task, cb) { | ||
// cancel the given task | ||
// uncaught cancellations errors bubbles upward | ||
task.done[CANCEL]( | ||
new SagaCancellationException(MANUAL_CANCEL, '', name) | ||
new SagaCancellationException(MANUAL_CANCEL, name, name) | ||
) | ||
return Promise.resolve() | ||
cb() | ||
// cancel effects are non cancellables | ||
} | ||
function runParallelEffect(effects, effectId) { | ||
const promises = effects.map(eff => runEffect(eff, effectId)) | ||
const ret = Promise.all(promises) | ||
ret[CANCEL] = error => { | ||
promises.forEach(p => cancelPromise(p, error)) | ||
// Reimplementing Promise.all. We're in 2016 | ||
function runParallelEffect(effects, effectId, cb) { | ||
let completedCount = 0 | ||
let completed | ||
const results = Array(effects.length) | ||
const childCbs = effects.map( (eff, idx) => { | ||
const chCbAtIdx = (err, res) => { | ||
// Either we've been cancelled, or an error aborted the whole effect | ||
if(completed) | ||
return | ||
// one of the effects failed | ||
if(err) { | ||
// cancel all other effects | ||
// This is an AUTO_CANCEL (not triggered by a manual cancel) | ||
// Catch uncaught cancellation errors, because w'll only throw the actual | ||
// rejection error (err) inside this generator | ||
try { | ||
cb.cancel( | ||
new SagaCancellationException(PARALLEL_AUTO_CANCEL, name, name) | ||
) | ||
} catch(err) { void(0) } | ||
cb(err) | ||
} else { | ||
results[idx] = res | ||
completedCount++ | ||
if(completedCount === results.length) { | ||
completed = true | ||
cb(null, results) | ||
} | ||
} | ||
} | ||
chCbAtIdx.cancel = noop | ||
return chCbAtIdx | ||
}) | ||
// This is different, a cancellation coming from upward | ||
// either a MANUAL_CANCEL or a parent AUTO_CANCEL | ||
// No need to catch, will be swallowed by the caller | ||
cb.cancel = cancelError => { | ||
// prevents unnecessary cancellation | ||
if(!completed) { | ||
completed = true | ||
childCbs.forEach(chCb => chCb.cancel(cancelError)) | ||
} | ||
} | ||
ret.catch(() => { | ||
ret[CANCEL]( | ||
new SagaCancellationException(PARALLEL_AUTO_CANCEL, name, name) | ||
) | ||
}) | ||
return ret | ||
effects.forEach( (eff, idx) => runEffect(eff, effectId, idx, childCbs[idx]) ) | ||
} | ||
function runRaceEffect(effects, effectId) { | ||
const promises = [] | ||
const retP = Promise.race( | ||
Object.keys(effects) | ||
.map(key => { | ||
const promise = runEffect(effects[key], effectId, key) | ||
promises.push(promise) | ||
return promise.then( | ||
result => ({[key]: result}), | ||
error => Promise.reject({[key]: error}) | ||
) | ||
}) | ||
) | ||
// And yet; Promise.race | ||
function runRaceEffect(effects, effectId, cb) { | ||
let completed | ||
const keys = Object.keys(effects) | ||
const childCbs = {} | ||
retP[CANCEL] = error => { | ||
promises.forEach(p => cancelPromise(p, error)) | ||
keys.forEach(key => { | ||
const chCbAtKey = (err, res) => { | ||
// Either we've been cancelled, or an error aborted the whole effect | ||
if(completed) | ||
return | ||
if(err) { | ||
// Race Auto cancellation | ||
try { | ||
cb.cancel( | ||
new SagaCancellationException(RACE_AUTO_CANCEL, name, name) | ||
) | ||
} catch(err) { void(0) } | ||
cb({[key]: err}) | ||
} else { | ||
try { | ||
cb.cancel( | ||
new SagaCancellationException(RACE_AUTO_CANCEL, name, name) | ||
) | ||
} catch(err) { void(0) } | ||
completed = true | ||
cb(null, {[key]: res}) | ||
} | ||
} | ||
chCbAtKey.cancel = noop | ||
childCbs[key] = chCbAtKey | ||
}) | ||
cb.cancel = cancelError => { | ||
// prevents unnecessary cancellation | ||
if(!completed) { | ||
completed = true | ||
keys.forEach(key => childCbs[key].cancel(cancelError)) | ||
} | ||
} | ||
const done = () => retP[CANCEL]( | ||
new SagaCancellationException(RACE_AUTO_CANCEL, name, name) | ||
) | ||
retP.then(done, done) | ||
return retP | ||
keys.forEach(key => runEffect(effects[key], effectId, key, childCbs[key])) | ||
} | ||
@@ -253,6 +418,2 @@ | ||
function cancelPromise(promise, error) { | ||
if(promise[CANCEL]) | ||
promise[CANCEL](error) | ||
} | ||
} |
@@ -5,2 +5,4 @@ export const TASK = Symbol('TASK') | ||
export const isDev = typeof process !== 'undefined' && process.env && process.env.NODE_ENV === 'development' | ||
export function check(value, predicate, error) { | ||
@@ -7,0 +9,0 @@ if(! predicate(value) ) |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
202282
31
2605
814