redux-saga
Advanced tools
Comparing version 0.6.1 to 0.7.0
@@ -64,3 +64,3 @@ (function webpackUniversalModuleDefinition(root, factory) { | ||
var _proc = __webpack_require__(3); | ||
var _proc = __webpack_require__(4); | ||
@@ -183,7 +183,7 @@ Object.defineProperty(exports, 'CANCEL', { | ||
var _SagaCancellationException2 = __webpack_require__(4); | ||
var _SagaCancellationException2 = __webpack_require__(2); | ||
var _SagaCancellationException3 = _interopRequireDefault(_SagaCancellationException2); | ||
var _monitorActions = __webpack_require__(2); | ||
var _monitorActions = __webpack_require__(3); | ||
@@ -304,2 +304,35 @@ var monitorActions = _interopRequireWildcard(_monitorActions); | ||
}); | ||
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 */ | ||
/***/ function(module, exports) { | ||
'use strict'; | ||
Object.defineProperty(exports, "__esModule", { | ||
value: true | ||
}); | ||
exports.effectTriggered = effectTriggered; | ||
@@ -335,3 +368,3 @@ exports.effectResolved = effectResolved; | ||
/***/ }, | ||
/* 3 */ | ||
/* 4 */ | ||
/***/ function(module, exports, __webpack_require__) { | ||
@@ -344,3 +377,3 @@ | ||
}); | ||
exports.MANUAL_CANCEL = exports.RACE_AUTO_CANCEL = exports.PARALLEL_AUTO_CANCEL = exports.CANCEL = exports.NOT_ITERATOR_ERROR = undefined; | ||
exports.MANUAL_CANCEL = exports.RACE_AUTO_CANCEL = exports.PARALLEL_AUTO_CANCEL = exports.CANCEL = exports.undefindInputError = exports.NOT_ITERATOR_ERROR = undefined; | ||
exports.default = proc; | ||
@@ -352,7 +385,7 @@ | ||
var _monitorActions = __webpack_require__(2); | ||
var _monitorActions = __webpack_require__(3); | ||
var monitorActions = _interopRequireWildcard(_monitorActions); | ||
var _SagaCancellationException = __webpack_require__(4); | ||
var _SagaCancellationException = __webpack_require__(2); | ||
@@ -368,2 +401,6 @@ var _SagaCancellationException2 = _interopRequireDefault(_SagaCancellationException); | ||
var NOT_ITERATOR_ERROR = exports.NOT_ITERATOR_ERROR = 'proc first argument (Saga function result) must be an iterator'; | ||
var undefindInputError = exports.undefindInputError = function undefindInputError(name) { | ||
return '\n ' + name + ' saga was provided with an undefined input action\n Hints :\n - check that your Action Creator returns a non undefined value\n - if the Saga was started using runSaga, check that your subscribe source provides the action to its listeners\n'; | ||
}; | ||
var CANCEL = exports.CANCEL = Symbol('@@redux-saga/cancelPromise'); | ||
@@ -387,2 +424,4 @@ var PARALLEL_AUTO_CANCEL = exports.PARALLEL_AUTO_CANCEL = 'PARALLEL_AUTO_CANCEL'; | ||
var UNDEFINED_INPUT_ERROR = undefindInputError(name); | ||
// tracks the current `take` effects | ||
@@ -396,2 +435,4 @@ var deferredInputs = []; | ||
var unsubscribe = subscribe(function (input) { | ||
if (input === undefined) throw UNDEFINED_INPUT_ERROR; | ||
for (var i = 0; i < deferredInputs.length; i++) { | ||
@@ -787,3 +828,8 @@ var def = deferredInputs[i]; | ||
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 _ref5 = {}, _defineProperty(_ref5, _utils.TASK, true), _defineProperty(_ref5, 'id', id), _defineProperty(_ref5, 'name', name), _defineProperty(_ref5, 'done', done), _defineProperty(_ref5, 'forked', forked), _defineProperty(_ref5, 'cancel', function cancel(error) { | ||
if (!(error instanceof _SagaCancellationException2.default)) { | ||
error = new _SagaCancellationException2.default(MANUAL_CANCEL, name, error); | ||
} | ||
done[CANCEL](error); | ||
}), _defineProperty(_ref5, 'isRunning', function isRunning() { | ||
return iterator._isRunning; | ||
@@ -799,35 +845,2 @@ }), _defineProperty(_ref5, 'getResult', function getResult() { | ||
/***/ }, | ||
/* 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; | ||
/***/ }, | ||
/* 5 */ | ||
@@ -1063,6 +1076,8 @@ /***/ function(module, exports, __webpack_require__) { | ||
}); | ||
exports.RUN_SAGA_DYNAMIC_ERROR = undefined; | ||
exports.default = sagaMiddlewareFactory; | ||
var _utils = __webpack_require__(1); | ||
var _proc = __webpack_require__(3); | ||
var _proc = __webpack_require__(4); | ||
@@ -1075,7 +1090,13 @@ var _proc2 = _interopRequireDefault(_proc); | ||
var _monitorActions = __webpack_require__(2); | ||
var _monitorActions = __webpack_require__(3); | ||
var _SagaCancellationException = __webpack_require__(2); | ||
var _SagaCancellationException2 = _interopRequireDefault(_SagaCancellationException); | ||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } | ||
exports.default = function () { | ||
var RUN_SAGA_DYNAMIC_ERROR = exports.RUN_SAGA_DYNAMIC_ERROR = 'Before running a Saga dynamically using middleware.run, you must mount the Saga middleware on the Store using applyMiddleware'; | ||
function sagaMiddlewareFactory() { | ||
for (var _len = arguments.length, sagas = Array(_len), _key = 0; _key < _len; _key++) { | ||
@@ -1085,3 +1106,5 @@ sagas[_key] = arguments[_key]; | ||
return function (_ref) { | ||
var runSagaDynamically = undefined; | ||
function sagaMiddleware(_ref) { | ||
var getState = _ref.getState; | ||
@@ -1097,6 +1120,14 @@ var dispatch = _ref.dispatch; | ||
sagas.forEach(function (saga) { | ||
(0, _proc2.default)(saga(getState), sagaEmitter.subscribe, dispatch, monitor, 0, saga.name); | ||
}); | ||
function runSaga(saga) { | ||
for (var _len2 = arguments.length, args = Array(_len2 > 1 ? _len2 - 1 : 0), _key2 = 1; _key2 < _len2; _key2++) { | ||
args[_key2 - 1] = arguments[_key2]; | ||
} | ||
return (0, _proc2.default)(saga.apply(undefined, [getState].concat(args)), sagaEmitter.subscribe, dispatch, monitor, 0, saga.name); | ||
} | ||
runSagaDynamically = runSaga; | ||
sagas.forEach(runSaga); | ||
return function (next) { | ||
@@ -1111,5 +1142,22 @@ return function (action) { | ||
}; | ||
} | ||
sagaMiddleware.run = function (saga) { | ||
for (var _len3 = arguments.length, args = Array(_len3 > 1 ? _len3 - 1 : 0), _key3 = 1; _key3 < _len3; _key3++) { | ||
args[_key3 - 1] = arguments[_key3]; | ||
} | ||
if (!runSagaDynamically) { | ||
throw new Error(RUN_SAGA_DYNAMIC_ERROR); | ||
} | ||
var task = runSagaDynamically.apply(undefined, [saga].concat(args)); | ||
task.done.catch(function (err) { | ||
if (!(err instanceof _SagaCancellationException2.default)) throw err; | ||
}); | ||
return task; | ||
}; | ||
}; | ||
return sagaMiddleware; | ||
} | ||
/***/ }, | ||
@@ -1130,3 +1178,3 @@ /* 8 */ | ||
var _proc = __webpack_require__(3); | ||
var _proc = __webpack_require__(4); | ||
@@ -1144,2 +1192,6 @@ var _proc2 = _interopRequireDefault(_proc); | ||
/** | ||
@deprecated | ||
ATTENTION! this method can have some potential issues | ||
For more infos, see issue https://github.com/yelouafi/redux-saga/issues/48 | ||
memoize the result of storeChannel. It avoids monkey patching the same store | ||
@@ -1151,2 +1203,7 @@ multiple times unnecessarly. We need only one channel per store | ||
if (_utils.isDev) { | ||
/* eslint-disable no-console */ | ||
console.warn('storeIO is deprecated, to run Saga dynamically, use \'run\' method of the middleware'); | ||
} | ||
if (store[IO]) return store[IO]; | ||
@@ -1153,0 +1210,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(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){function u(){o===c.length&&(a=!0,t(null,c))}if(!e.length)return void t(null,[]);var o=0,a=void 0,c=Array(e.length),f=e.map(function(e,n){var r=function(e,r){if(!a)if(e){try{t.cancel(new d.default(v,I,I))}catch(e){}t(e)}else c[n]=r,o++,u()};return r.cancel=i.noop,r});t.cancel=function(e){a||(a=!0,f.forEach(function(n){return n.cancel(e)}))},e.forEach(function(e,t){return r(e,n,t,f[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}}])}); | ||
!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(4);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(2),d=u(l),p=t(3),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){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){"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,I,"",n)}catch(u){i.isDev&&console.warn(k+": uncaught",u),t(u,!0)}}function t(n,t){e._isRunning=!1,t?(e._error=n,M.reject(n)):(e._result=n,M.resolve(n)),F()}function r(e,n){function t(e,n){d||(d=!0,o.cancel=i.noop,P(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=y();P(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),P(s.effectRejected(a,e))}};var p=void 0;return i.is.promise(e)?u(e,t):i.is.iterator(e)?f(e,a,k,t):i.is.array(e)?L(e,a,t):i.is.notUndef(p=c.as.take(e))?l(p,t):i.is.notUndef(p=c.as.put(e))?E(p,t):i.is.notUndef(p=c.as.race(e))?T(p,a,t):i.is.notUndef(p=c.as.call(e))?_(p,a,t):i.is.notUndef(p=c.as.cps(e))?R(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))?C(p,t):t(null,e)}function u(e,n){var t=e[v];"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,P,n,t).done,r)}function l(e,n){var t={match:(0,c.matcher)(e),pattern:e,resolve:function(e){return n(null,e)}};U.push(t),n.cancel=function(){return(0,i.remove)(U,t)}}function E(e,n){(0,i.asap)(function(){return n(null,j(e))})}function _(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 R(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,P,n,u.name,!0))}function b(e,n){u(e.done,n)}function C(e,n){e.done[v](new d.default(h,k,k)),n()}function L(e,n,t){function u(){o===c.length&&(a=!0,t(null,c))}if(!e.length)return void t(null,[]);var o=0,a=void 0,c=Array(e.length),f=e.map(function(e,n){var r=function(e,r){if(!a)if(e){try{t.cancel(new d.default(m,k,k))}catch(e){}t(e)}else c[n]=r,o++,u()};return r.cancel=i.noop,r});t.cancel=function(e){a||(a=!0,f.forEach(function(n){return n.cancel(e)}))},e.forEach(function(e,t){return r(e,n,t,f[t])})}function T(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(A,k,k))}catch(n){}t(o({},e,n))}else{try{t.cancel(new d.default(A,k,k))}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 w(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,"cancel",function(e){e instanceof d.default||(e=new d.default(h,n,e)),r[v](e)}),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],P=arguments.length<=3||void 0===arguments[3]?i.noop:arguments[3],I=arguments.length<=4||void 0===arguments[4]?0:arguments[4],k=arguments.length<=5||void 0===arguments[5]?"anonymous":arguments[5];(0,i.check)(e,i.is.iterator,p);var S=g(k),U=[],x=i.is.throw(e),M=(0,i.deferred)(),F=N(function(e){if(void 0===e)throw S;for(var n=0;n<U.length;n++){var t=U[n];t.match(e)&&(U=[],t.resolve(e))}});n.cancel=i.noop;var D=w(I,k,e,M.promise);return D.done[v]=function(e){var t=e.type,r=e.origin;n.cancel(new d.default(t,k,r))},e._isRunning=!0,n(),D}Object.defineProperty(n,"__esModule",{value:!0}),n.MANUAL_CANCEL=n.RACE_AUTO_CANCEL=n.PARALLEL_AUTO_CANCEL=n.CANCEL=n.undefindInputError=n.NOT_ITERATOR_ERROR=void 0,n.default=a;var i=t(1),c=t(6),f=t(3),s=u(f),l=t(2),d=r(l),p=n.NOT_ITERATOR_ERROR="proc first argument (Saga function result) must be an iterator",g=n.undefindInputError=function(e){return"\n "+e+" saga was provided with an undefined input action\n Hints :\n - check that your Action Creator returns a non undefined value\n - if the Saga was started using runSaga, check that your subscribe source provides the action to its listeners\n"},v=n.CANCEL=Symbol("@@redux-saga/cancelPromise"),m=n.PARALLEL_AUTO_CANCEL="PARALLEL_AUTO_CANCEL",A=n.RACE_AUTO_CANCEL="RACE_AUTO_CANCEL",h=n.MANUAL_CANCEL="MANUAL_CANCEL",y=(0,i.autoInc)()},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(E);return j(R,m.is.undef(e)?"*":e)}function a(e){return j(O,e)}function i(e){return j(b,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 j(C,c(e,t))}function s(e,n){var t=arguments.length<=2||void 0===arguments[2]?[]:arguments[2];return j(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 j(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 j(T,c(e,t))}function p(e){if(!I(e))throw new Error(h);return j(w,e)}function g(e){if(!I(e))throw new Error(y);return j(N,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}",h=(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)"),y=n.CANCEL_ARG_ERROR="cancel argument must be a valid task (a result of a fork)",E=n.INVALID_PATTERN="Invalid pattern passed to `take` (HINT: check if you didn't mispell a constant)",_=Symbol("IO"),R="TAKE",O="PUT",b="RACE",C="CALL",L="CPS",T="FORK",w="JOIN",N="CANCEL",j=function(e,n){var t;return t={},r(t,_,!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[_]&&e[R]},put:function(e){return e&&e[_]&&e[O]},race:function(e){return e&&e[_]&&e[b]},call:function(e){return e&&e[_]&&e[C]},cps:function(e){return e&&e[_]&&e[L]},fork:function(e){return e&&e[_]&&e[T]},join:function(e){return e&&e[_]&&e[w]},cancel:function(e){return e&&e[_]&&e[N]}}},function(e,n,t){"use strict";function r(e){return e&&e.__esModule?e:{"default":e}}function u(){function e(e){function n(e){for(var n=arguments.length,t=Array(n>1?n-1:0),u=1;n>u;u++)t[u-1]=arguments[u];return(0,i.default)(e.apply(void 0,[r].concat(t)),c.subscribe,a,l,0,e.name)}var r=e.getState,a=e.dispatch,c=(0,f.default)(),l=o.isDev?function(e){return(0,o.asap)(function(){return a(e)})}:void 0;return u=n,t.forEach(n),function(e){return function(n){var t=e(n);return n[s.MONITOR_ACTION]||c.emit(n),t}}}for(var n=arguments.length,t=Array(n),r=0;n>r;r++)t[r]=arguments[r];var u=void 0;return e.run=function(e){for(var n=arguments.length,t=Array(n>1?n-1:0),r=1;n>r;r++)t[r-1]=arguments[r];if(!u)throw new Error(p);var o=u.apply(void 0,[e].concat(t));return o.done.catch(function(e){if(!(e instanceof d.default))throw e}),o},e}Object.defineProperty(n,"__esModule",{value:!0}),n.RUN_SAGA_DYNAMIC_ERROR=void 0,n.default=u;var o=t(1),a=t(4),i=r(a),c=t(5),f=r(c),s=t(3),l=t(2),d=r(l),p=n.RUN_SAGA_DYNAMIC_ERROR="Before running a Saga dynamically using middleware.run, you must mount the Saga middleware on the Store using applyMiddleware"},function(e,n,t){"use strict";function r(e){return e&&e.__esModule?e:{"default":e}}function u(e){if(a.isDev&&console.warn("storeIO is deprecated, to run Saga dynamically, use 'run' method of the middleware"),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(4),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,2 +6,4 @@ 'use strict'; | ||
}); | ||
exports.RUN_SAGA_DYNAMIC_ERROR = undefined; | ||
exports.default = sagaMiddlewareFactory; | ||
@@ -20,5 +22,11 @@ var _utils = require('./utils'); | ||
var _SagaCancellationException = require('./SagaCancellationException'); | ||
var _SagaCancellationException2 = _interopRequireDefault(_SagaCancellationException); | ||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } | ||
exports.default = function () { | ||
var RUN_SAGA_DYNAMIC_ERROR = exports.RUN_SAGA_DYNAMIC_ERROR = 'Before running a Saga dynamically using middleware.run, you must mount the Saga middleware on the Store using applyMiddleware'; | ||
function sagaMiddlewareFactory() { | ||
for (var _len = arguments.length, sagas = Array(_len), _key = 0; _key < _len; _key++) { | ||
@@ -28,3 +36,5 @@ sagas[_key] = arguments[_key]; | ||
return function (_ref) { | ||
var runSagaDynamically = undefined; | ||
function sagaMiddleware(_ref) { | ||
var getState = _ref.getState; | ||
@@ -40,6 +50,14 @@ var dispatch = _ref.dispatch; | ||
sagas.forEach(function (saga) { | ||
(0, _proc2.default)(saga(getState), sagaEmitter.subscribe, dispatch, monitor, 0, saga.name); | ||
}); | ||
function runSaga(saga) { | ||
for (var _len2 = arguments.length, args = Array(_len2 > 1 ? _len2 - 1 : 0), _key2 = 1; _key2 < _len2; _key2++) { | ||
args[_key2 - 1] = arguments[_key2]; | ||
} | ||
return (0, _proc2.default)(saga.apply(undefined, [getState].concat(args)), sagaEmitter.subscribe, dispatch, monitor, 0, saga.name); | ||
} | ||
runSagaDynamically = runSaga; | ||
sagas.forEach(runSaga); | ||
return function (next) { | ||
@@ -54,3 +72,20 @@ return function (action) { | ||
}; | ||
} | ||
sagaMiddleware.run = function (saga) { | ||
for (var _len3 = arguments.length, args = Array(_len3 > 1 ? _len3 - 1 : 0), _key3 = 1; _key3 < _len3; _key3++) { | ||
args[_key3 - 1] = arguments[_key3]; | ||
} | ||
if (!runSagaDynamically) { | ||
throw new Error(RUN_SAGA_DYNAMIC_ERROR); | ||
} | ||
var task = runSagaDynamically.apply(undefined, [saga].concat(args)); | ||
task.done.catch(function (err) { | ||
if (!(err instanceof _SagaCancellationException2.default)) throw err; | ||
}); | ||
return task; | ||
}; | ||
}; | ||
return sagaMiddleware; | ||
} |
@@ -6,3 +6,3 @@ 'use strict'; | ||
}); | ||
exports.MANUAL_CANCEL = exports.RACE_AUTO_CANCEL = exports.PARALLEL_AUTO_CANCEL = exports.CANCEL = exports.NOT_ITERATOR_ERROR = undefined; | ||
exports.MANUAL_CANCEL = exports.RACE_AUTO_CANCEL = exports.PARALLEL_AUTO_CANCEL = exports.CANCEL = exports.undefindInputError = exports.NOT_ITERATOR_ERROR = undefined; | ||
exports.default = proc; | ||
@@ -29,2 +29,6 @@ | ||
var NOT_ITERATOR_ERROR = exports.NOT_ITERATOR_ERROR = 'proc first argument (Saga function result) must be an iterator'; | ||
var undefindInputError = exports.undefindInputError = function undefindInputError(name) { | ||
return '\n ' + name + ' saga was provided with an undefined input action\n Hints :\n - check that your Action Creator returns a non undefined value\n - if the Saga was started using runSaga, check that your subscribe source provides the action to its listeners\n'; | ||
}; | ||
var CANCEL = exports.CANCEL = Symbol('@@redux-saga/cancelPromise'); | ||
@@ -48,2 +52,4 @@ var PARALLEL_AUTO_CANCEL = exports.PARALLEL_AUTO_CANCEL = 'PARALLEL_AUTO_CANCEL'; | ||
var UNDEFINED_INPUT_ERROR = undefindInputError(name); | ||
// tracks the current `take` effects | ||
@@ -57,2 +63,4 @@ var deferredInputs = []; | ||
var unsubscribe = subscribe(function (input) { | ||
if (input === undefined) throw UNDEFINED_INPUT_ERROR; | ||
for (var i = 0; i < deferredInputs.length; i++) { | ||
@@ -448,3 +456,8 @@ var def = deferredInputs[i]; | ||
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 _ref5 = {}, _defineProperty(_ref5, _utils.TASK, true), _defineProperty(_ref5, 'id', id), _defineProperty(_ref5, 'name', name), _defineProperty(_ref5, 'done', done), _defineProperty(_ref5, 'forked', forked), _defineProperty(_ref5, 'cancel', function cancel(error) { | ||
if (!(error instanceof _SagaCancellationException2.default)) { | ||
error = new _SagaCancellationException2.default(MANUAL_CANCEL, name, error); | ||
} | ||
done[CANCEL](error); | ||
}), _defineProperty(_ref5, 'isRunning', function isRunning() { | ||
return iterator._isRunning; | ||
@@ -451,0 +464,0 @@ }), _defineProperty(_ref5, 'getResult', function getResult() { |
@@ -25,2 +25,6 @@ 'use strict'; | ||
/** | ||
@deprecated | ||
ATTENTION! this method can have some potential issues | ||
For more infos, see issue https://github.com/yelouafi/redux-saga/issues/48 | ||
memoize the result of storeChannel. It avoids monkey patching the same store | ||
@@ -32,2 +36,7 @@ multiple times unnecessarly. We need only one channel per store | ||
if (_utils.isDev) { | ||
/* eslint-disable no-console */ | ||
console.warn('storeIO is deprecated, to run Saga dynamically, use \'run\' method of the middleware'); | ||
} | ||
if (store[IO]) return store[IO]; | ||
@@ -34,0 +43,0 @@ |
{ | ||
"name": "redux-saga", | ||
"version": "0.6.1", | ||
"version": "0.7.0", | ||
"description": "Saga middleware for Redux to handle Side Effects", | ||
@@ -20,3 +20,8 @@ "main": "lib/index.js", | ||
"async": "node examples/async/server.js", | ||
"real-world": "node examples/real-world/server.js" | ||
"real-world": "node examples/real-world/server.js", | ||
"docs:clean": "rimraf _book", | ||
"docs:prepare": "gitbook install", | ||
"docs:build": "npm run docs:prepare && gitbook build -g yelouafi/redux-saga", | ||
"docs:watch": "npm run docs:prepare && gitbook serve", | ||
"docs:publish": "npm run docs:clean && npm run docs:build && cd _book && git init && git commit --allow-empty -m 'update book' && git checkout -b gh-pages && touch .nojekyll && git add . && git commit -am 'update book' && git push git@github.com:yelouafi/redux-saga gh-pages --force" | ||
}, | ||
@@ -53,2 +58,3 @@ "repository": { | ||
"express": "^4.13.3", | ||
"gitbook-cli": "1.0.1", | ||
"isomorphic-fetch": "^2.2.0", | ||
@@ -55,0 +61,0 @@ "react": "^0.14.3", |
@@ -85,9 +85,9 @@ # redux-saga | ||
const createStoreWithSaga = applyMiddleware( | ||
// ..., | ||
sagaMiddleware(...sagas) | ||
)(createStore) | ||
export default function configureStore(initialState) { | ||
return createStoreWithSaga(reducer, initialState) | ||
// Note: passing middleware as the last argument to createStore requires redux@>=3.1.0 | ||
return createStore( | ||
reducer, | ||
initialState, | ||
applyMiddleware(/* other middleware, */sagaMiddleware(...sagas)) | ||
} | ||
} | ||
@@ -94,0 +94,0 @@ ``` |
@@ -83,9 +83,9 @@ # redux-saga | ||
const createStoreWithSaga = applyMiddleware( | ||
// ..., | ||
sagaMiddleware(...sagas) | ||
)(createStore) | ||
export default function configureStore(initialState) { | ||
return createStoreWithSaga(reducer, initialState) | ||
// Note: passing middleware as the last argument to createStore requires redux@>=3.1.0 | ||
return createStore( | ||
reducer, | ||
initialState, | ||
applyMiddleware(/* other middleware, */sagaMiddleware(...sagas)) | ||
} | ||
} | ||
@@ -92,0 +92,0 @@ ``` |
757
README.md
@@ -17,3 +17,3 @@ # redux-saga | ||
> As you'll see in the rest of this README, Generators, while seemingly more low-level than ES7 async | ||
> As you'll see in the docs, Generators, while seemingly more low-level than ES7 async | ||
functions, allow some features like declarative effects and cancellation which are harder—if not | ||
@@ -40,21 +40,4 @@ impossible—to implement with simple async functions. | ||
- [Getting started](#getting-started) | ||
- [Waiting for future actions](#waiting-for-future-actions) | ||
- [Dispatching actions to the store](#dispatching-actions-to-the-store) | ||
- [A common abstraction: Effect](#a-common-abstraction-effect) | ||
- [Declarative Effects](#declarative-effects) | ||
- [Error handling](#error-handling) | ||
- [Effect Combinators](#effect-combinators) | ||
- [Sequencing Sagas via yield*](#sequencing-sagas-via-yield) | ||
- [Composing Sagas](#composing-sagas) | ||
- [Non blocking calls with fork/join](#non-blocking-calls-with-forkjoin) | ||
- [Task cancellation](#task-cancellation) | ||
- [Dynamically starting Sagas with runSaga](#dynamically-starting-sagas-with-runsaga) | ||
- [Building examples from sources](#building-examples-from-sources) | ||
- [Using umd build in the browser](#using-umd-build-in-the-browser) | ||
# Install | ||
#Getting started | ||
Install | ||
``` | ||
@@ -64,706 +47,39 @@ npm install redux-saga | ||
Create the Saga (using the counter example from Redux) | ||
```javascript | ||
import { take, put } from 'redux-saga' | ||
// sagas/index.js | ||
# Documentation | ||
function* incrementAsync() { | ||
- [Introduction](http://yelouafi.github.io/redux-saga/docs/introduction/index.html) | ||
- [Basic Concepts](http://yelouafi.github.io/redux-saga/docs/basics/index.html) | ||
- [Advanced Concepts](http://yelouafi.github.io/redux-saga/docs/advanced/index.html) | ||
- [Recipes](http://yelouafi.github.io/redux-saga/docs/recipes/index.html) | ||
- [External Resources](http://yelouafi.github.io/redux-saga/docs/ExternalResources.html) | ||
- [Troubleshooting](http://yelouafi.github.io/redux-saga/docs/Troubleshooting.html) | ||
- [Glossary](http://yelouafi.github.io/redux-saga/docs/Glossary.html) | ||
- [API Reference](http://yelouafi.github.io/redux-saga/docs/api/index.html) | ||
while(true) { | ||
# Using umd build in the browser | ||
// wait for each INCREMENT_ASYNC action | ||
const nextAction = yield take(INCREMENT_ASYNC) | ||
There's also an **umd** build of `redux-saga` available in the `dist/` folder. When using the umd build | ||
`redux-saga` is available as `ReduxSaga` in the window object. | ||
// delay is a sample function | ||
// return a Promise that resolves after (ms) milliseconds | ||
yield delay(1000) | ||
The umd version is useful if you don't use Webpack or Browserify. You can access it directly from [npmcdn](npmcdn.com). | ||
// dispatch INCREMENT_COUNTER | ||
yield put( increment() ) | ||
} | ||
The following builds are available: | ||
} | ||
- [https://npmcdn.com/redux-saga/dist/redux-saga.js](https://npmcdn.com/redux-saga/dist/redux-saga.js) | ||
- [https://npmcdn.com/redux-saga/dist/redux-saga.min.js](https://npmcdn.com/redux-saga/dist/redux-saga.min.js) | ||
export default [incrementAsync] | ||
``` | ||
**Important!** If the browser you are targeting doesn't support _es2015 generators_ you must provide a valid polyfill, | ||
for example the one provided by *babel*: | ||
[browser-polyfill.min.js](https://cdnjs.cloudflare.com/ajax/libs/babel-core/5.8.25/browser-polyfill.min.js). | ||
The polyfill must be imported before **redux-saga**. | ||
Plug redux-saga in the middleware pipeline | ||
```javascript | ||
// store/configureStore.js | ||
import 'babel-polyfill' | ||
// then | ||
import sagaMiddleware from 'redux-saga' | ||
import sagas from '../sagas' | ||
const createStoreWithSaga = applyMiddleware( | ||
// ..., | ||
sagaMiddleware(...sagas) | ||
)(createStore) | ||
export default function configureStore(initialState) { | ||
return createStoreWithSaga(reducer, initialState) | ||
} | ||
``` | ||
#Waiting for future actions | ||
# Building examples from sources | ||
In the previous example we created an `incrementAsync` Saga. The call `yield take(INCREMENT_ASYNC)` is an | ||
illustration of how Sagas typically work. | ||
Typically, actual middlewares handle some Effect form triggered by an Action Creator. For example, | ||
redux-thunk handles *thunks* by calling them with `(getState, dispatch)` as arguments, | ||
redux-promise handles Promises by dispatching their resolved values, and redux-gen handles generators by | ||
dispatching all yielded actions to the store. The common thing that all those middlewares share is the | ||
same 'call on each action' pattern. They will be called again and again each time an action happens, | ||
i.e. they are *scoped* by the *root action* that triggered them. | ||
Sagas work differently, they are not fired from within Action Creators but are started with your | ||
application and choose what user actions to watch. They are like daemon tasks that run in | ||
the background and choose their own logic of progression. In the example above, `incrementAsync` *pulls* | ||
the `INCREMENT_ASYNC` action using `yield take(...)`. This is a *blocking call*, which means the Saga | ||
will not progress until it receives a matching action. | ||
Above, we used the form `take(INCREMENT_ASYNC)`, which means we're waiting for an action whose type | ||
is `INCREMENT_ASYNC`. | ||
`take` support some more patterns to constrain future actions matching. A call of `yield take(PATTERN)` will be | ||
handled using the following rules | ||
- If PATTERN is undefined or `'*'` all incoming actions are matched (e.g. `take()` will match all actions) | ||
- 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.) | ||
- 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`). | ||
#Dispatching actions to the store | ||
After receiving the queried action, the Saga triggers a call to `delay(1000)`, which in our example | ||
returns a Promise that will resolve after 1 second. This is a blocking call, so the Saga | ||
will wait for 1 second before continuing on. | ||
After the delay, the Saga dispatches an `INCREMENT_COUNTER` action using the `put(action)` | ||
function. Here also, the Saga will wait for the dispatch result. If the dispatch call returns | ||
a normal value, the Saga resumes *immediately* (ASAP), but if the result value is a Promise then the | ||
Saga will wait until the Promise is resolved (or rejected). | ||
#A common abstraction: Effect | ||
To generalize: waiting for a future action, waiting for the future result of a function call like | ||
`yield delay(1000)`, or waiting for the result of a dispatch all are the same concept. In all cases, | ||
we are yielding some form of Effects. | ||
What a Saga does is actually compose all those effects together to implement the desired control flow. | ||
The simplest is to sequence yielded Effects by just putting the yields one after another. You can also use the | ||
familiar control flow operators (`if`, `while`, `for`) to implement more sophisticated control flows. Or you | ||
you can use the provided Effects combinators to express concurrency (yield race) and parallelism (yield [...]). | ||
You can even yield calls to other Sagas, allowing the powerful routine/subroutine pattern. | ||
For example, `incrementAsync` uses an infinite loop `while(true)` which means it will stay alive | ||
for the entirety of the application's lifetime. | ||
You can also create Sagas that last a limited amount of time. For example, the following Saga | ||
waits for the first 3 `INCREMENT_COUNTER` actions, triggers a `showCongratulation()` action and then finishes. | ||
```javascript | ||
function* onBoarding() { | ||
for(let i = 0; i < 3; i++) | ||
yield take(INCREMENT_COUNTER) | ||
yield put( showCongratulation() ) | ||
} | ||
``` | ||
#Declarative Effects | ||
Sagas Generators can yield Effects in multiple forms. The simplest way is to yield a Promise. | ||
```javascript | ||
function* fetchSaga() { | ||
// fetch is a sample function | ||
// returns a Promise that will resolve with the GET response | ||
const products = yield fetch('/products') | ||
// dispatch a RECEIVE_PRODUCTS action | ||
yield put( receiveProducts(products) ) | ||
} | ||
``` | ||
In the example above, `fetch('/products')` returns a Promise that will resolve with the GET response, | ||
so the 'fetch effect' will be executed immediately. Simple and idiomatic but... | ||
Suppose we want to test generator above: | ||
```javascript | ||
const iterator = fetchSaga() | ||
assert.deepEqual( iterator.next().value, ?? ) // what do we expect ? | ||
``` | ||
We want to check the result of the first value yielded by the generator, which is in our case the result of running | ||
`fetch('/products')`. Executing the real service during tests is neither a viable nor practical approach, so we have to | ||
*mock* the fetch service, i.e. we'll have to replace the real `fetch` method with a fake one which doesn't actually | ||
run the GET request but only checks that we've called `fetch` with the right arguments (`'/products'` in our case). | ||
Mocks make testing more difficult and less reliable. On the other hand, functions that simply return values are | ||
easier to test, since we can use a simple `equal()` to check the result. This is the way to write the most reliable tests. | ||
Not convinced? I encourage you to read this [Eric Elliott' article] | ||
(https://medium.com/javascript-scene/what-every-unit-test-needs-f6cd34d9836d#.4ttnnzpgc) | ||
>(...)`equal()`, by nature answers the two most important questions every unit test must answer, but most don’t: | ||
- What is the actual output? | ||
- What is the expected output? | ||
>If you finish a test without answering those two questions, you don’t have a real unit test. You have a sloppy, half-baked test. | ||
What we actually need is just to make sure the `fetchSaga` yields a call with the right function and the right | ||
arguments. For this reason, the library provides some declarative ways to yield Side Effects while still making it | ||
easy to test the Saga logic | ||
```javascript | ||
import { call } from 'redux-saga' | ||
function* fetchSaga() { | ||
const products = yield call( fetch, '/products' ) // don't run the effect | ||
} | ||
``` | ||
We're using now the `call(fn, ...args)` function. **The difference from the preceeding example is that now we're not | ||
executing the fetch call immediately, instead, `call` creates a description of the effect**. Just as in | ||
Redux you use action creators to create a plain object describing the action that will get executed by the Store, | ||
`call` creates a plain object describing the function call. The redux-saga middleware takes care of executing | ||
the function call and resuming the generator with the resolved response. | ||
This allows us to easily test the Generator outside the Redux environment. | ||
```javascript | ||
import { call } from 'redux-saga' | ||
const iterator = fetchSaga() | ||
assert.deepEqual(iterator.next().value, call(fetch, '/products')) // expects a call(...) value | ||
``` | ||
Now we don't need to mock anything, and a simple equality test will suffice. | ||
The advantage of declarative effects is that we can test all the logic inside a Saga/Generator | ||
by simply iterating over the resulting iterator and doing a simple equality tests on the values | ||
yielded successively. This is a real benefit, as your complex asynchronous operations are no longer | ||
black boxes, and you can test in detail their operational logic no matter how complex it is. | ||
To invoke methods of some object (i.e. created with `new`), you can provide a `this` context to the | ||
invoked functions using the following form: | ||
```javascript | ||
yield call([obj, obj.method], arg1, arg2, ...) // as if we did obj.method(arg1, arg2 ...) | ||
``` | ||
`apply` is an alias for the method invocation form | ||
```javascript | ||
yield apply(obj, obj.method, [arg1, arg2, ...]) | ||
``` | ||
`call` and `apply` are well suited for functions that return Promise results. Another function | ||
`cps` can be used to handle Node style functions (e.g. `fn(...args, callback)` where `callback` | ||
is of the form `(error, result) => ()`). For example: | ||
```javascript | ||
import { cps } from 'redux-saga' | ||
const content = yield cps(readFile, '/path/to/file') | ||
``` | ||
and of course you can test it just like you test call: | ||
```javascript | ||
import { cps } from 'redux-saga' | ||
const iterator = fetchSaga() | ||
assert.deepEqual(iterator.next().value, cps(readFile, '/path/to/file') ) | ||
``` | ||
`cps` supports also the same method invocation form as `call`. | ||
#Error handling | ||
You can catch errors inside the Generator using the simple try/catch syntax. In the following example, | ||
the Saga catch errors from the `api.buyProducts` call (i.e. a rejected Promise) | ||
```javascript | ||
function* checkout(getState) { | ||
while( yield take(types.CHECKOUT_REQUEST) ) { | ||
try { | ||
const cart = getState().cart | ||
yield call(api.buyProducts, cart) | ||
yield put(actions.checkoutSuccess(cart)) | ||
} catch(error) { | ||
yield put(actions.checkoutFailure(error)) | ||
} | ||
} | ||
} | ||
``` | ||
Of course you're not forced to handle you API errors inside `try`/`catch` blocks, you can also make | ||
your API service return a normal value with some error flag on it. | ||
```javascript | ||
function buyProducts(cart) { | ||
return doPost(...) | ||
.then(result => {result}) | ||
.catch(error => {error}) | ||
} | ||
function* checkout(getState) { | ||
while( yield take(types.CHECKOUT_REQUEST) ) { | ||
const cart = getState().cart | ||
const {result, error} = yield call(api.buyProducts, cart) | ||
if(!error) | ||
yield put(actions.checkoutSuccess(result)) | ||
else | ||
yield put(actions.checkoutFailure(error)) | ||
} | ||
} | ||
``` | ||
#Effect Combinators | ||
The `yield` statement is great for representing asynchronous control flow in a simple and linear | ||
style, but we also need to do things in parallel. We can't simply write: | ||
```javascript | ||
// Wrong, effects will be executed in sequence | ||
const users = yield call(fetch, '/users'), | ||
repose = yield call(fetch, '/repose') | ||
``` | ||
Because the 2nd effect will not get executed until the first call resolves. Instead we have to write: | ||
```javascript | ||
import { call } from 'redux-saga' | ||
// correct, effects will get executed in parallel | ||
const [users, repose] = yield [ | ||
call(fetch, '/users'), | ||
call(fetch, '/repose') | ||
] | ||
``` | ||
When we yield an array of effects, the generator is blocked until all the effects are resolved (or as soon as | ||
one is rejected, just like how `Promise.all` behaves). | ||
Sometimes we start multiple tasks in parallel but we don't want to wait for all of them, we just need | ||
to get the *winner*: the first one that resolves (or rejects). The `race` function offers a way of | ||
triggering a race between multiple effects. | ||
The following sample shows a Saga that triggers a remote fetch request, and constrain the response with a | ||
1 second timeout. | ||
```javascript | ||
import { race, take, put } from 'redux-saga' | ||
function* fetchPostsWithTimeout() { | ||
while( yield take(FETCH_POSTS) ) { | ||
// starts a race between 2 effects | ||
const {posts, timeout} = yield race({ | ||
posts : call(fetchApi, '/posts'), | ||
timeout : call(delay, 1000) | ||
}) | ||
if(posts) | ||
put( actions.receivePosts(posts) ) | ||
else | ||
put( actions.timeoutError() ) | ||
} | ||
} | ||
``` | ||
#Sequencing Sagas via `yield*` | ||
You can use the builtin `yield*` operator to compose multiple sagas in a sequential way. | ||
This allows you to sequence your *macro-tasks* in a simple procedural style. | ||
```javascript | ||
function* playLevelOne(getState) { ... } | ||
function* playLevelTwo(getState) { ... } | ||
function* playLevelThree(getState) { ... } | ||
function* game(getState) { | ||
const score1 = yield* playLevelOne(getState) | ||
put(showScore(score1)) | ||
const score2 = yield* playLevelTwo(getState) | ||
put(showScore(score2)) | ||
const score3 = yield* playLevelThree(getState) | ||
put(showScore(score3)) | ||
} | ||
``` | ||
Note that using `yield*` will cause the JavaScript runtime to *spread* the whole sequence. | ||
The resulting iterator (from `game()`) will yield all values from the nested | ||
iterators. A more powerful alternative is to use the more generic middleware composition mechanism. | ||
#Composing Sagas | ||
While using `yield*` provides an idiomatic way of composing Sagas, this approach has some limitations: | ||
- You'll likely want to test nested generators separately. This leads to some duplication in the test | ||
code as well as the overhead of the duplicated execution. We don't want to execute a nested generator | ||
but only make sure the call to it was issued with the right argument. | ||
- More importantly, `yield*` allows only for sequential composition of tasks, so you can only | ||
yield* to one generator at a time. | ||
You can simply use `yield` to start one or more subtasks in parallel. When yielding a call to a | ||
generator, the Saga will wait for the generator to terminate before progressing, then resume | ||
with the returned value (or throws if an error propagates from the subtask). | ||
```javascript | ||
function* fetchPosts() { | ||
yield put( actions.requestPosts() ) | ||
const products = yield call(fetchApi, '/products') | ||
yield put( actions.receivePosts(products) ) | ||
} | ||
function* watchFetch() { | ||
while ( yield take(FETCH_POSTS) ) { | ||
yield call(fetchPosts) // waits for the fetchPosts task to terminate | ||
} | ||
} | ||
``` | ||
Yielding to an array of nested generators will start all the sub-generators in parallel and wait | ||
for them to finish. Then resume with all the results | ||
```javascript | ||
function* mainSaga(getState) { | ||
const results = yield [ call(task1), call(task2), ...] | ||
yield put( showResults(results) ) | ||
} | ||
``` | ||
In fact, yielding Sagas is no different than yielding other effects (future actions, timeouts, etc). | ||
This means you can combine those Sagas with all the other types using the effect combinators. | ||
For example you may want the user to finish some game in a limited amount of time: | ||
```javascript | ||
function* game(getState) { | ||
let finished | ||
while(!finished) { | ||
// has to finish in 60 seconds | ||
const {score, timeout} = yield race({ | ||
score : call( play, getState), | ||
timeout : call(delay, 60000) | ||
}) | ||
if(!timeout) { | ||
finished = true | ||
yield put( showScore(score) ) | ||
} | ||
} | ||
} | ||
``` | ||
#Non blocking calls with fork/join | ||
the `yield` statement causes the generator to pause until the yielded effect resolves or rejects. | ||
If you look closely at this example: | ||
```javascript | ||
function* watchFetch() { | ||
while ( yield take(FETCH_POSTS) ) { | ||
yield put( actions.requestPosts() ) | ||
const posts = yield call(fetchApi, '/posts') // blocking call | ||
yield put( actions.receivePosts(posts) ) | ||
} | ||
} | ||
``` | ||
the `watchFetch` generator will wait until `yield call(fetchApi, '/posts')` terminates. Imagine that the | ||
`FETCH_POSTS` action is fired from a `Refresh` button. If our application disables the button between | ||
each fetch (no concurrent fetches) then there is no issue, because we know that no `FETCH_POSTS` action | ||
will occur until we get the response from the `fetchApi` call. | ||
But what happens if the application allows the user to click on `Refresh` without waiting for the | ||
current request to terminate? | ||
The following example illustrates a possible sequence of the events | ||
``` | ||
UI watchFetch | ||
-------------------------------------------------------- | ||
FETCH_POSTS.....................call fetchApi........... waiting to resolve | ||
........................................................ | ||
........................................................ | ||
FETCH_POSTS............................................. missed | ||
........................................................ | ||
FETCH_POSTS............................................. missed | ||
................................fetchApi returned....... | ||
........................................................ | ||
``` | ||
When `watchFetch` is blocked on the `fetchApi` call, all `FETCH_POSTS` occurring in between the | ||
call and the response are missed. | ||
To express non-blocking calls, we can use the `fork` function. A possible rewrite of the previous example | ||
with `fork` can be: | ||
```javascript | ||
import { fork, call, take, put } from 'redux-saga' | ||
function* fetchPosts() { | ||
yield put( actions.requestPosts() ) | ||
const posts = yield call(fetchApi, '/posts') | ||
yield put( actions.receivePosts(posts) ) | ||
} | ||
function* watchFetch() { | ||
while ( yield take(FETCH_POSTS) ) { | ||
yield fork(fetchPosts) // non blocking call | ||
} | ||
} | ||
``` | ||
`fork`, just like `call`, accepts function/generator calls. | ||
```javascript | ||
yield fork(func, ...args) // simple async functions (...) -> Promise | ||
yield fork(generator, ...args) // Generator functions | ||
``` | ||
The result of `yield fork(api)` is a *Task descriptor*. To get the result of a forked Task | ||
in a later time, we use the `join` function | ||
```javascript | ||
import { fork, join } from 'redux-saga' | ||
function* child() { ... } | ||
function *parent() { | ||
// non blocking call | ||
const task = yield fork(subtask, ...args) | ||
// ... later | ||
// now a blocking call, will resume with the outcome of task | ||
const result = yield join(task) | ||
} | ||
``` | ||
the task object exposes some useful methods | ||
<table> | ||
<tr> | ||
<th>method</th> | ||
<th>return value</th> | ||
</tr> | ||
<tr> | ||
<td>task.isRunning()</td> | ||
<td>true if the task hasn't yet returned or throwed an error</td> | ||
</tr> | ||
<tr> | ||
<td>task.result()</td> | ||
<td>task return value. `undefined` if task is still running</td> | ||
</tr> | ||
<tr> | ||
<td>task.error()</td> | ||
<td>task thrown error. `undefined` if task is still running</td> | ||
</tr> | ||
<tr> | ||
<td>task.done</td> | ||
<td> | ||
a Promise which is either | ||
<ul> | ||
<li>resolved with task's return value</li> | ||
<li>rejected with task's thrown error</li> | ||
</ul> | ||
</td> | ||
</tr> | ||
</table> | ||
#Task cancellation | ||
Once a task is forked, you can abort its execution using `yield cancel(task)`. Cancelling | ||
a running task will throw a `SagaCancellationException` inside it. | ||
To see how it works, let's consider a simple example. A background sync which can be | ||
started/stopped by some UI commands. Upon receiving a `START_BACKGROUND_SYNC` action, | ||
we fork a background task that will periodically sync some data from a remote server. | ||
The task will execute continually until a `STOP_BACKGROUND_SYNC` action is triggered. | ||
Then we cancel the background task and wait again for the next `START_BACKGROUND_SYNC` action. | ||
```javascript | ||
import { take, put, call, fork, cancel, SagaCancellationException } from 'redux-saga' | ||
import actions from 'somewhere' | ||
import { someApi, delay } from 'somewhere' | ||
function* bgSync() { | ||
try { | ||
while(true) { | ||
yield put(actions.requestStart()) | ||
const result = yield call(someApi) | ||
yield put(actions.requestSuccess(result)) | ||
yield call(delay, 5000) | ||
} | ||
} catch(error) { | ||
if(error instanceof SagaCancellationException) | ||
yield put(actions.requestFailure('Sync cancelled!')) | ||
} | ||
} | ||
function* main() { | ||
while( yield take(START_BACKGROUND_SYNC) ) { | ||
// starts the task in the background | ||
const bgSyncTask = yield fork(bgSync) | ||
// wait for the user stop action | ||
yield take(STOP_BACKGROUND_SYNC) | ||
// user clicked stop. cancel the background task | ||
// this will throw a SagaCancellationException into the forked bgSync task | ||
yield cancel(bgSyncTask) | ||
} | ||
} | ||
``` | ||
`yield cancel(bgSyncTask)` will throw a `SagaCancellationException` | ||
inside the currently running task. In the above example, the exception is caught by | ||
`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). | ||
Cancelling a running task will also cancel the current effect where the task is blocked | ||
at the moment of cancellation. | ||
For example, suppose that at a certain point in an application's lifetime, we had this pending call chain: | ||
```javascript | ||
function* main() { | ||
const task = yield fork(subtask) | ||
... | ||
// later | ||
yield cancel(task) | ||
} | ||
function* subtask() { | ||
... | ||
yield call(subtask2) // currently blocked on this call | ||
... | ||
} | ||
function* subtask2() { | ||
... | ||
yield call(someApi) // currently blocked on this all | ||
... | ||
} | ||
``` | ||
`yield cancel(task)` will trigger a cancellation on `subtask`, which in turn will trigger | ||
a cancellation on `subtask2`. A `SagaCancellationException` will be thrown inside `subtask2`, | ||
then another `SagaCancellationException` will be thrown inside `subtask`. If `subtask` | ||
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'`). | ||
The main purpose of the cancellation exception is to allow cancelled tasks to perform any | ||
cleanup logic, so we wont leave the application in an inconsistent state. In the above example | ||
of background sync, by catching the cancellation exception, `bgSync` is able to dispatch a | ||
`requestFailure` action to the store. Otherwise, the store could be left in a inconsistent | ||
state (e.g. waiting for the result of a pending request). | ||
>It's important to remember that `yield cancel(task)` doesn't wait for the cancelled task | ||
to finish (i.e. to perform its catch block). The cancel effect behave like fork. It returns | ||
as soon as the cancel was initiated. | ||
>Once cancelled, a task should normally return as soon as it finishes its cleanup logic. | ||
In some cases, the cleanup logic could involve some async operations, but the cancelled | ||
task lives now as a separate process, and there is no way for it to rejoin the main | ||
control flow (except dispatching actions for other tasks via the Redux store. However | ||
this will lead to complicated control flows that are hard to reason about. It's always preferable | ||
to terminate a cancelled task ASAP). | ||
##Automatic cancellation | ||
Besides manual cancellation there are cases where cancellation is triggered automatically | ||
1- In a `race` effect. All race competitors, except the winner, are automatically cancelled. | ||
2- In a parallel effect (`yield [...]`). The parallel effect is rejected as soon as one of the | ||
sub-effects is rejected (as implied by `Promise.all`). In this case, all the other sub-effects | ||
are automatically cancelled. | ||
#Dynamically starting Sagas with runSaga | ||
The `runSaga` function allows starting sagas outside the Redux middleware environment. It also | ||
allows you to hook up to external input/output, other than store actions. | ||
For example, you can start a Saga on the server using: | ||
```javascript | ||
import serverSaga from 'somewhere' | ||
import {runSaga, storeIO} from 'redux-saga' | ||
import configureStore from 'somewhere' | ||
import rootReducer from 'somewhere' | ||
const store = configureStore(rootReducer) | ||
runSaga( | ||
serverSaga(store.getState), | ||
storeIO(store) | ||
).done.then(...) | ||
``` | ||
`runSaga` returns a task object. Just like the one returned from a `fork` effect. | ||
Besides taking and dispatching actions to the store `runSaga` can also be connected to | ||
other input/output sources. This allows you to exploit all the features of sagas to implement | ||
control flows outside Redux. | ||
The method has the following signature | ||
```javascript | ||
runSaga(iterator, {subscribe, dispatch}, [monitor]) | ||
``` | ||
Arguments | ||
- `iterator: {next, throw}` : an iterator object, Typically created by invoking a Generator function | ||
- `subscribe(callback) => unsubscribe`: i.e. a function which accepts a callback and returns an unsubscribe function | ||
- `callback(action)` : callback (provided by runSaga) used to subscribe to input events. `subscribe` must | ||
support registering multiple subscriptions | ||
- `unsubscribe()` : used by `runSaga` to unsubscribe from the input source once it | ||
has completed (either by normal return or a thrown exception) | ||
- `dispatch(action) => result`: used to fulfill `put` effects. Each time a `yield put(action)` is issued, `dispatch` | ||
is invoked with `action`. The return value of `dispatch` is used to fulfill the `put` effect. Promise results | ||
are automatically resolved/rejected. | ||
- `monitor(sagaAction)` (optional): a callback which is used to dispatch all Saga related events. In the middleware | ||
version, all actions are dispatched to the Redux store. See the [sagaMonitor example] | ||
(https://github.com/yelouafi/redux-saga/blob/master/examples/sagaMonitor.js) for usage. | ||
The `subscribe` argument is used to fulfill `take(action)` effects. Each time `subscribe` emits an action | ||
to its callbacks, all sagas that are blocked on `take(PATTERN)`, and whose take pattern matches the currently incoming action, | ||
are resumed with that action. | ||
#Building examples from sources | ||
``` | ||
git clone https://github.com/yelouafi/redux-saga.git | ||
@@ -777,3 +93,4 @@ cd redux-saga | ||
Counter example | ||
### Counter example | ||
``` | ||
@@ -786,3 +103,4 @@ npm run counter | ||
Shopping Cart example | ||
### Shopping Cart example | ||
``` | ||
@@ -795,3 +113,4 @@ npm run shop | ||
async example | ||
### async example | ||
``` | ||
@@ -803,3 +122,4 @@ npm run async | ||
real-world example (with webpack hot reloading) | ||
### real-world example (with webpack hot reloading) | ||
``` | ||
@@ -810,12 +130,1 @@ cd examples/real-world | ||
``` | ||
#Using umd build in the browser | ||
There's an **umd** build of `redux-saga` available in the `dist/` folder. When using the umd build `redux-saga` is available as `ReduxSaga` in the window object. | ||
The umd version is useful if you don't use webpack or browserify. You can access it directly from [npmcdn](npmcdn.com). | ||
The following builds are available: | ||
- [https://npmcdn.com/redux-saga/dist/redux-saga.js](https://npmcdn.com/redux-saga/dist/redux-saga.js) | ||
- [https://npmcdn.com/redux-saga/dist/redux-saga.min.js](https://npmcdn.com/redux-saga/dist/redux-saga.min.js) | ||
**Important!** If the browser you are targeting doesn't support _es2015 generators_ you must provide a valid polyfill, for example the one provided by *babel*: [browser-polyfill.min.js](https://cdnjs.cloudflare.com/ajax/libs/babel-core/5.8.25/browser-polyfill.min.js). The polyfill must be imported before **redux-saga**. |
@@ -5,31 +5,52 @@ import { asap, isDev } from './utils' | ||
import { MONITOR_ACTION } from './monitorActions' | ||
import SagaCancellationException from './SagaCancellationException' | ||
export const RUN_SAGA_DYNAMIC_ERROR = 'Before running a Saga dynamically using middleware.run, you must mount the Saga middleware on the Store using applyMiddleware' | ||
export default (...sagas) => ({getState, dispatch}) => { | ||
export default function sagaMiddlewareFactory(...sagas) { | ||
let runSagaDynamically | ||
const sagaEmitter = emitter() | ||
const monitor = isDev ? action => asap(() => dispatch(action)) : undefined | ||
function sagaMiddleware({getState, dispatch}) { | ||
sagas.forEach( saga => { | ||
proc( | ||
saga(getState), | ||
sagaEmitter.subscribe, | ||
dispatch, | ||
monitor, | ||
0, | ||
saga.name | ||
) | ||
}) | ||
const sagaEmitter = emitter() | ||
const monitor = isDev ? action => asap(() => dispatch(action)) : undefined | ||
return next => action => { | ||
const result = next(action) // hit reducers | ||
// 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; | ||
function runSaga(saga, ...args) { | ||
return proc( | ||
saga(getState, ...args), | ||
sagaEmitter.subscribe, | ||
dispatch, | ||
monitor, | ||
0, | ||
saga.name | ||
) | ||
} | ||
runSagaDynamically = runSaga | ||
sagas.forEach(runSaga) | ||
return next => action => { | ||
const result = next(action) // hit reducers | ||
// 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; | ||
} | ||
} | ||
sagaMiddleware.run = (saga, ...args) => { | ||
if(!runSagaDynamically) { | ||
throw new Error(RUN_SAGA_DYNAMIC_ERROR) | ||
} | ||
const task = runSagaDynamically(saga, ...args) | ||
task.done.catch(err => { | ||
if(!(err instanceof SagaCancellationException)) | ||
throw err | ||
}) | ||
return task | ||
} | ||
return sagaMiddleware | ||
} |
@@ -8,2 +8,9 @@ import { noop, is, isDev, check, remove, deferred, autoInc, asap, TASK } from './utils' | ||
export const NOT_ITERATOR_ERROR = 'proc first argument (Saga function result) must be an iterator' | ||
export const undefindInputError = name => ` | ||
${name} saga was provided with an undefined input action | ||
Hints : | ||
- check that your Action Creator returns a non undefined value | ||
- if the Saga was started using runSaga, check that your subscribe source provides the action to its listeners | ||
` | ||
export const CANCEL = Symbol('@@redux-saga/cancelPromise') | ||
@@ -27,2 +34,4 @@ export const PARALLEL_AUTO_CANCEL = 'PARALLEL_AUTO_CANCEL' | ||
const UNDEFINED_INPUT_ERROR = undefindInputError(name) | ||
// tracks the current `take` effects | ||
@@ -36,2 +45,5 @@ let deferredInputs = [] | ||
const unsubscribe = subscribe(input => { | ||
if(input === undefined) | ||
throw UNDEFINED_INPUT_ERROR | ||
for (let i = 0; i < deferredInputs.length; i++) { | ||
@@ -420,2 +432,8 @@ const def = deferredInputs[i] | ||
forked, | ||
cancel: error => { | ||
if(!(error instanceof SagaCancellationException)) { | ||
error = new SagaCancellationException(MANUAL_CANCEL, name, error) | ||
} | ||
done[CANCEL](error) | ||
}, | ||
isRunning: () => iterator._isRunning, | ||
@@ -422,0 +440,0 @@ getResult: () => iterator._result, |
@@ -1,2 +0,2 @@ | ||
import { is, check, noop } from './utils' | ||
import { is, check, noop, isDev } from './utils' | ||
import proc from './proc' | ||
@@ -8,2 +8,6 @@ import emitter from './emitter' | ||
/** | ||
@deprecated | ||
ATTENTION! this method can have some potential issues | ||
For more infos, see issue https://github.com/yelouafi/redux-saga/issues/48 | ||
memoize the result of storeChannel. It avoids monkey patching the same store | ||
@@ -15,2 +19,7 @@ multiple times unnecessarly. We need only one channel per store | ||
if(isDev) { | ||
/* eslint-disable no-console */ | ||
console.warn(`storeIO is deprecated, to run Saga dynamically, use 'run' method of the middleware`) | ||
} | ||
if(store[IO]) | ||
@@ -17,0 +26,0 @@ return store[IO] |
Sorry, the diff of this file is not supported yet
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
2754
185075
23
123