redux-saga 0.14.6
概论
redux-saga包用于监听redux派发action事件,以生成器函数的书写方式触发一系列程式执行。
当特定action被派发dispatch时,redux-saga机制借助proc、io模块执行task任务流。该task任务流允许借助io.take方法挂起task任务流,当特定action被派发时予以执行后续流程;io.put方法派发action;io.race同步触发多个延迟函数,以先执行完成的延迟函数构建返回对象;io.call/apply/cps执行同步或异步函数(返回值为延迟对象promise);io.fork创建子task任务流;io.join获取子task的返回值;io.select收集state等。
redux-saga的实现,在redux中间件中使用sagaEmitter.emit(action)方法触发事件,通过sagaEmitter.subscribe添加绑定函数。具体实现为,redux-saga借助sagaMiddleware.run(saga)方法将saga封装为一个task任务流,task任务流为生成器书写形式,首次调用next方法为初始化,剩余next方法通过io.take方法或sagaHelpers工具函数添加为sagaEmitter.subscribe的绑定函数,以实现redux派发action时触发task后续流程的执行。
基本使用
创建saga
io.take方式创建saga
import { take, put } from 'redux-saga/effects'
function* incrementAsync() {
while(true) {
// 由io.take挂起task任务流,直到redux派发INCREMENT_ASYNC action时触发后续流程执行
const nextAction = yield take(INCREMENT_ASYNC)
// 执行延迟函数,返回promise对象
yield delay(1000)
// 派发 INCREMENT_COUNTER
yield put({type:"INCREMENT_COUNTER"})
}
}
export default [incrementAsync]
sagaHelpers方式创建saga
import { put, call } from 'redux-saga/effects'
import { delay, takeEvery } from 'redux-saga'
export function* incrementAsync() {
yield call(delay, 1000)
yield put({type: 'INCREMENT'})
}
export default function* rootSaga() {
// 由sagaHelpers.takeEvery挂起task任务流,直到redux派发INCREMENT_ASYNC action时触发后续流程执行
yield takeEvery('INCREMENT_ASYNC', incrementAsync)
}
redux中间件挂载redux-saga
import sagaMiddleware from 'redux-saga'
import sagas from '../sagas'
export default function configureStore(initialState) {
// Note: passing middleware as the last argument to createStore requires redux@>=3.1.0
let store = createStore(
reducer,
initialState,
applyMiddleware(/* other middleware, */sagaMiddleware())
}
sagaMiddleware.run(sagas)
return store
}
核心源码
middleware.js挂载redux中间件,添加saga
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = sagaMiddlewareFactory;
var _utils = require('./utils');
var _proc = require('./proc');
var _proc2 = _interopRequireDefault(_proc);
var _channel = require('./channel');
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
function _toConsumableArray(arr) {
if (Array.isArray(arr)) {
for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) {
arr2[i] = arr[i];
}
return arr2;
} else {
return Array.from(arr);
}
}
// sagaMiddlewareFactory(
// options={
// sagaMonitor:{
// effectTriggered,// 加载saga或派发action获得effect时调用执行,可用于打印日志
// effectResolved,effectRejected,
// effectCancelled,// 加载的某个saga,其next方法执行到TASK_CANCEL参数时触发调用,可用于打印日志
// actionDispatched:(action)=>{}// action由redux机制派发时触发执行的函数
// },
// logger:()=>{},
// onerror:()=>{},
// 用于装饰sagaEmitter.emit触发订阅者函数,action为传递给各订阅者函数的参数,即redux机制下的action
// emitter:(emit)=>{return (action)=>{console.log(action);emit(action)}},
// }
// )
function sagaMiddlewareFactory() {
var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
var runSagaDynamically = void 0;
var sagaMonitor = options.sagaMonitor;
// monitors are expected to have a certain interface, let's fill-in any missing ones
if (sagaMonitor) {
sagaMonitor.effectTriggered = sagaMonitor.effectTriggered || _utils.noop;
sagaMonitor.effectResolved = sagaMonitor.effectResolved || _utils.noop;
sagaMonitor.effectRejected = sagaMonitor.effectRejected || _utils.noop;
sagaMonitor.effectCancelled = sagaMonitor.effectCancelled || _utils.noop;
sagaMonitor.actionDispatched = sagaMonitor.actionDispatched || _utils.noop;
}
if (_utils.is.func(options)) {
if (process.env.NODE_ENV === 'production') {
throw new Error('Saga middleware no longer accept Generator functions. Use sagaMiddleware.run instead');
} else {
throw new Error('You passed a function to the Saga middleware. You are likely trying to start a '
+ ' Saga by directly passing it to the middleware.'
+ ' This is no longer possible starting from 0.10.0.'
+ ' To run a Saga, you must do it dynamically AFTER mounting the middleware into the store.\n Example:\n import createSagaMiddleware from \'redux-saga\'\n ... other imports\n\n const sagaMiddleware = createSagaMiddleware()\n const store = createStore(reducer, applyMiddleware(sagaMiddleware))\n sagaMiddleware.run(saga, ...args)\n ');
}
}
if (options.logger && !_utils.is.func(options.logger)) {
throw new Error('`options.logger` passed to the Saga middleware is not a function!');
}
if (options.onerror) {
if (_utils.isDev) (0, _utils.log)('warn', '`options.onerror` is deprecated. Use `options.onError` instead.');
options.onError = options.onerror;
delete options.onerror;
}
if (options.onError && !_utils.is.func(options.onError)) {
throw new Error('`options.onError` passed to the Saga middleware is not a function!');
}
if (options.emitter && !_utils.is.func(options.emitter)) {
throw new Error('`options.emitter` passed to the Saga middleware is not a function!');
}
// 由redux中间件机制传入dispatch派发action方法、和getState获取state方法
function sagaMiddleware(_ref) {
var getState = _ref.getState,
dispatch = _ref.dispatch;
runSagaDynamically = runSaga;
// channel.emitter返回{subscribe,emit}用于添加或触发订阅者函数
var sagaEmitter = (0, _channel.emitter)();
sagaEmitter.emit = (options.emitter || _utils.ident)(sagaEmitter.emit);// 装饰sagaEmitter.emit
// 将dispatch获得的参数action更迭为object.assign(action,{'@@redux-saga/SAGA_ACTION':true})
var sagaDispatch = (0, _utils.wrapSagaDispatch)(dispatch);
// 添加saga,saga书写时需用io.take将saga.next后续流程挂起,使用sagaEmitter.emit触发
// 挂起操作也可以借助sagaHelper工具集中各工具函数实现
function runSaga(saga, args, sagaId) {
// sagaEmitter.subscribe添加订阅者函数,sagaDispatch派发action,getState获取state
// options为redux-saga的middleware获得的配置项,sagaId第几个saga
return (0, _proc2.default)(
saga.apply(undefined, _toConsumableArray(args)),
sagaEmitter.subscribe, sagaDispatch, getState, options, sagaId, saga.name);
}
return function (next) {
return function (action) {
if (sagaMonitor) {
sagaMonitor.actionDispatched(action);
}
// 交由下一个redux中间件处理
var result = next(action); // hit reducers
// 将action交由sagaEmitter.subscribe方法添加的订阅者函数处理
sagaEmitter.emit(action);
return result;
};
};
}
sagaMiddleware.run = function (saga) {
for (var _len = arguments.length, args = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
args[_key - 1] = arguments[_key];
}
(0, _utils.check)(runSagaDynamically, _utils.is.notUndef, 'Before running a Saga, you must mount the Saga middleware on the Store using applyMiddleware');
(0, _utils.check)(saga, _utils.is.func, 'sagaMiddleware.run(saga, ...args): saga argument must be a Generator function!');
// 标识符,第几个saga
var effectId = (0, _utils.uid)();
if (sagaMonitor) {
sagaMonitor.effectTriggered({ effectId: effectId, root: true, parentEffectId: 0, effect: { root: true, saga: saga, args: args } });
}
var task = runSagaDynamically(saga, args, effectId);
if (sagaMonitor) {
sagaMonitor.effectResolved(effectId, task);
}
return task;
};
return sagaMiddleware;
}
proc.js支配saga的运作流程
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.TASK_CANCEL = exports.CHANNEL_END = exports.NOT_ITERATOR_ERROR = undefined;
var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ?
function (obj) { return typeof obj; } :
function (obj) {
return obj && typeof Symbol === "function" && obj.constructor === Symbol &&
obj !== Symbol.prototype ? "symbol" : typeof obj;
};
exports.default = proc;
var _utils = require('./utils');
var _scheduler = require('./scheduler');
var _io = require('./io');
var _channel = require('./channel');
var _buffers = require('./buffers');
// 设定属性描述符
function _defineEnumerableProperties(obj, descs) {
for (var key in descs) {
var desc = descs[key];
desc.configurable = desc.enumerable = true;
if ("value" in desc) desc.writable = true;
Object.defineProperty(obj, key, desc);
}
return obj;
}
function _toConsumableArray(arr) {
if (Array.isArray(arr)) {
for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) {
arr2[i] = arr[i];
}
return arr2;
} else {
return Array.from(arr);
}
}
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 NOT_ITERATOR_ERROR = exports.NOT_ITERATOR_ERROR = 'proc first argument (Saga function result) must be an iterator';
var CHANNEL_END = exports.CHANNEL_END = {
toString: function toString() {
return '@@redux-saga/CHANNEL_END';
}
};
var TASK_CANCEL = exports.TASK_CANCEL = {
toString: function toString() {
return '@@redux-saga/TASK_CANCEL';
}
};
var matchers = {
wildcard: function wildcard() {
return _utils.kTrue;
},
default: function _default(pattern) {
return function (input) {
return input.type === ((typeof pattern === 'undefined' ? 'undefined'
: _typeof(pattern)) === 'symbol' ? pattern : String(pattern));
};
},
array: function array(patterns) {
return function (input) {
return patterns.some(function (p) {
return matcher(p)(input);
});
};
},
predicate: function predicate(_predicate) {
return function (input) {
return _predicate(input);
};
}
};
// 构建匹配函数;判断是否通配符无条件,或数组的某一项,或满足某类型,或匹配某函数
function matcher(pattern) {
return (pattern === '*' ? matchers.wildcard :
_utils.is.array(pattern) ? matchers.array :
_utils.is.stringableFunc(pattern) ? matchers.default :
_utils.is.func(pattern) ? matchers.predicate : matchers.default)(pattern);
}
// 返回{addTask,cancelAll,abort,getTasks,taskNames}用于添加task函数
// 报错时清空tasks函数集合,执行cb(err,true);成功时执行cb(res),mainTask.cont对res不作处理
// mainTask的意义:proc设计的迭代流程控制函数next执行到TASK_CANCEL或CHANNEL_END时,跳回到mainTask.cont处理最终响应res
// next执行过程报错时,也跳回到mainTask.cont处理错误err
// 通过mainTask.cont调用end函数,最终启用saga._deferredEnd.resolve|reject处理成功或失败状态
function forkQueue(name, mainTask, cb) {
var tasks = [],
result = void 0,
completed = false;
// 为mainTask添加cont方法,mainTask.cont在成功时将res顺序传入cb回调;报错时执行cb(err,true),清空tasks
addTask(mainTask);
// completed为真返回;否则,各task函数重置cont方法、并执行cancel方法,清空tasks中缓存的各task函数
// 最终调用cb处理err,cb次参传入true
function abort(err) {
cancelAll();
cb(err, true);
}
// 添加task(task函数由proc函数的返回值构成),为task函数添加cont方法
function addTask(task) {
tasks.push(task);
// completed为真返回;否则,报错时清空tasks,调用cb(res,true),或者调用cb处理res
task.cont = function (res, isErr) {
if (completed) {
return;
}
(0, _utils.remove)(tasks, task);
task.cont = _utils.noop;
if (isErr) {
// 各task函数重置cont方法、并执行cancel方法,清空tasks中缓存的各task函数;最终调用cb(res,true)
abort(res);
} else {
if (task === mainTask) {
result = res;
}
if (!tasks.length) {
completed = true;
cb(result);
}
}
};
}
// completed为真返回;否则,各task函数重置cont方法、并执行cancel方法,清空tasks中缓存的各task函数
function cancelAll() {
if (completed) {
return;
}
completed = true;
tasks.forEach(function (t) {
t.cont = _utils.noop;
t.cancel();
});
tasks = [];
}
return {
addTask: addTask,
cancelAll: cancelAll,
abort: abort,
getTasks: function getTasks() {
return tasks;
},
taskNames: function taskNames() {
return tasks.map(function (t) {
return t.name;
});
}
};
}
// 构建迭代器
function createTaskIterator(_ref) {
var context = _ref.context,
fn = _ref.fn,
args = _ref.args;
if (_utils.is.iterator(fn)) {
return fn;
}
var result = void 0,
error = void 0;
try {
result = fn.apply(context, args);
} catch (err) {
error = err;
}
if (_utils.is.iterator(result)) {
return result;
}
return error ? (0, _utils.makeIterator)(function () {
throw error;
}) : (0, _utils.makeIterator)(function () {
var pc = void 0;
var eff = { done: false, value: result };
var ret = function ret(value) {
return { done: true, value: value };
};
return function (arg) {
if (!pc) {
pc = true;
return eff;
} else {
return ret(arg);
}
};
}());
}
var wrapHelper = function wrapHelper(helper) {
return { fn: helper };
};
// proc(iterator,subscribe,dispatch,getState,options,parentEffectId,name)
// 参数subscribe即sagaEmitter.subscribe,添加订阅者函数,sagaDispatch派发action,getState获取state
// sagaEmitter.emit方法在订阅者函数的回调cb执行过程中,间接执行takers缓存的各获取者函数
function proc(iterator) {
var subscribe = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : function () {
return _utils.noop;
};
var dispatch = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : _utils.noop;
var getState = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : _utils.noop;
var options = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : {};
var parentEffectId = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : 0;
var name = arguments.length > 6 && arguments[6] !== undefined ? arguments[6] : 'anonymous';
var cont = arguments[7];
(0, _utils.check)(iterator, _utils.is.iterator, NOT_ITERATOR_ERROR);
var sagaMonitor = options.sagaMonitor,
logger = options.logger,
onError = options.onError;
var log = logger || _utils.log;
// channel.stdChannel(subscribe) 返回{take,flush,close}
// take(cb,matcher)方法缓存taker获取者函数cb,并向cb添加'@@redux-saga/MATCH'属性为次参matcher
// flush方法无操作,close方法关闭chan,并清空订阅者函数
// sagaEmitter.emit方法在订阅者函数的回调cb执行过程中,间接执行takers缓存的各获取者函数
var stdChannel = (0, _channel.stdChannel)(subscribe);
// 由各runForkEffect等函数进行赋值,流程终止时调用
next.cancel = _utils.noop;
// newTask函数返回
// {
// '@@redux-saga/TASK': true,
// id: parentEffectId,
// name: name,
// cont: cont,
// joiners: [],
// cancel: cancel,
// isRunning: ()=>iterator._isRunning,
// isCancelled: ()=>iterator._isCancelled,
// isAborted: ()=>iterator._isAborted,
// result: ()=>iterator._result,
// error: ()=>iterator._error,
// get done 属性修饰符约定只能获取,返回promise对象
// }
var task = newTask(parentEffectId, name, iterator, cont);
// proc设计的迭代流程控制函数next执行到TASK_CANCEL或CHANNEL_END时,跳回到mainTask.cont处理最终响应res
// next执行过程报错时,也跳回到mainTask.cont处理错误err
// 通过mainTask.cont调用end函数,最终启用saga._deferredEnd.resolve|reject处理成功或失败状态
var mainTask = { name: name, cancel: cancelMain, isRunning: true };
// forkQueue(name,mainTask,cb)返回{addTask,cancelAll,abort,getTasks,taskNames}用于添加task函数
// 报错时清空tasks函数集合,执行cb(err,true);成功时执行cb(res),mainTask.cont对res不作处理
// forkQueue函数为mainTask添加cont方法,mainTask.cont在成功时将res顺序传入cb回调;报错时执行cb(err,true),清空tasks
var taskQueue = forkQueue(name, mainTask, end);
function cancelMain() {
if (mainTask.isRunning && !mainTask.isCancelled) {
mainTask.isCancelled = true;
next(TASK_CANCEL);
}
}
function cancel() {
if (iterator._isRunning && !iterator._isCancelled) {
iterator._isCancelled = true;
taskQueue.cancelAll();
end(TASK_CANCEL);
}
}
cont && (cont.cancel = cancel);
// 标记iterator即saga的_isRunning为true,iterator也可能是子task的iterator
// mainTask在整个生命周期中存活,子task自在调用时存活
iterator._isRunning = true;
// 执行saga;或当saga为sagaHelper中工具函数创建或调用了io.take方法,将saga.next方法挂起,由middleware模块派发action时触发
next();
return task;
// 初始化执行saga.next
// 或者当saga为sagaHelper中工具函数创建或调用了io.take方法,将saga.next方法挂起,由middleware模块派发action时触发执行
function next(arg, isErr) {
if (!mainTask.isRunning) {
throw new Error('Trying to resume an already finished generator');
}
try {
var result = void 0;
if (isErr) {
result = iterator.throw(arg);
} else if (arg === TASK_CANCEL) {
mainTask.isCancelled = true;
// 由各runForkEffect等函数进行赋值,流程终止时调用
next.cancel();
result = _utils.is.func(iterator.return) ? iterator.return(TASK_CANCEL) : { done: true, value: TASK_CANCEL };
} else if (arg === CHANNEL_END) {
result = _utils.is.func(iterator.return) ? iterator.return() : { done: true };
} else {
result = iterator.next(arg);
}
if (!result.done) {
runEffect(result.value, parentEffectId, '', next);
} else {
// proc设计的迭代流程控制函数next执行到TASK_CANCEL或CHANNEL_END时,表示迭代终结
// 调用mainTask处理最终返回值result.value
mainTask.isMainRunning = false;
mainTask.cont && mainTask.cont(result.value);
}
} catch (error) {
if (mainTask.isCancelled) {
log('error', 'uncaught at ' + name, error.message);
}
mainTask.isMainRunning = false;
// 清空forkQueue中tasks函数集合,并将error顺序传递给end函数,并执行end(error,true)
mainTask.cont(error, true);
}
}
// iterator即为saga,调用saga._deferredEnd.resolve处理成功时的响应,saga._deferredEnd.reject处理错误
// task.cont不存在时,执行钩子middleware模块sagaMiddlewareFactory的参数option.onError方法
// 成功或失败时,都执行task.cont方法、task.joiners函数集合并清空task.joiners函数集合
// 业务上,end函数作为forkQueue函数的尾参,
function end(result, isErr) {
iterator._isRunning = false;
// 取出stdChannel函数中takers缓存中的各获取者函数,以参数{type:'@@redux-saga/CHANNEL_END'}逐个执行
// 并清空sagaEmitter中的订阅者函数
stdChannel.close();
if (!isErr) {
if (result === TASK_CANCEL && _utils.isDev) {
log('info', name + ' has been cancelled', '');
}
iterator._result = result;
iterator._deferredEnd && iterator._deferredEnd.resolve(result);
} else {
if (result instanceof Error) {
result.sagaStack = 'at ' + name + ' \n ' + (result.sagaStack || result.stack);
}
if (!task.cont) {
log('error', 'uncaught', result.sagaStack || result.stack);
if (result instanceof Error && onError) {
onError(result);
}
}
iterator._error = result;
iterator._isAborted = true;
iterator._deferredEnd && iterator._deferredEnd.reject(result);
}
task.cont && task.cont(result, isErr);
task.joiners.forEach(function (j) {
return j.cb(result, isErr);
});
task.joiners = null;
}
// proc的流程控制函数next接受传参不是TASK_CANCEL或CHANNEL_END时,调用runEffect函数处理saga.next(arg)返回值effect
// 根据effect的类型控制不同的处理逻辑,如effect为{IO:true,TAKE:{}}形式时,调用runTakeEffect函数作处理
// runEffect(effect,parentEffectId,label,cb)
// 参数effect为saga.next(arg)返回值,cb为next函数执行saga.next或处理最终返回值或错误
function runEffect(effect, parentEffectId) {
var label = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : '';
var cb = arguments[3];
var effectId = (0, _utils.uid)();
sagaMonitor && sagaMonitor.effectTriggered({ effectId: effectId, parentEffectId: parentEffectId, label: label, effect: effect });
var effectSettled = void 0;
// 通过currCb调用next函数,接着执行interator.next即saga.next方法
function currCb(res, isErr) {
if (effectSettled) {
return;
}
effectSettled = true;
cb.cancel = _utils.noop;
if (sagaMonitor) {
isErr ? sagaMonitor.effectRejected(effectId, res) : sagaMonitor.effectResolved(effectId, res);
}
// cb即proc下next流程控制函数
cb(res, isErr);
}
// currCb.cancel更迭为interator.next即saga.next的再次执行后赋值
// interator.next即saga.next得到参数为TASK_CANCEL时,通过next.cancel调用currCb.cancel方法
currCb.cancel = _utils.noop;
// interator.next即saga.next方法执行的参数为TASK_CANCEL,终止当前的saga;cb即next函数
cb.cancel = function () {
// currCb执行后,effectSettled设置为true,互斥关系
if (effectSettled) {
return;
}
effectSettled = true;
try {
currCb.cancel();
} catch (err) {
log('error', 'uncaught at ' + name, err.message);
}
currCb.cancel = _utils.noop;
sagaMonitor && sagaMonitor.effectCancelled(effectId);
};
var data = void 0;
return (
// interator.next即saga.next方法执行时返回promise延迟对象,将currCb作为promise对象的回调并执行
_utils.is.promise(effect) ? resolvePromise(effect, currCb) :
// interator.next即saga.next方法执行时返回sagaHelpers构建的迭代器,构建子task
// effect为调用sagaHelpers.takeEveryHelper | takeLatestHelper | throttleHelper 方法构建的迭代器
// 通常proc函数的入参intertor即saga就是sagaHelpers.takeEveryHelper等方法构建的迭代器{name,next,throw,return}
_utils.is.helper(effect) ? runForkEffect(wrapHelper(effect), effectId, currCb) :
// interator.next即saga.next方法执行时返回迭代器,递归调用proc函数构建子task
_utils.is.iterator(effect) ? resolveIterator(effect, effectId, name, currCb) :
// interator.next即saga.next方法执行时返回数组,对数组中每个子项迭代调用runEffect函数
// 每个runEffect函数的返回值串接成数组,该数组作为currCb的参数
_utils.is.array(effect) ? runParallelEffect(effect, effectId, currCb) :
// effect为{IO:true,TAKE:{}}形式,io.asEffect.take获取effect的TAKE属性{channel}或{pattern}
// 该情形在interator.next即saga.next中调用io.take方法时触发
// 或者当saga为sagaHelpers.takeEveryHelper|takeLatestHelper|throttleHelper方法构建的迭代器
// 意义是挂起currCb;当redux派发action时,再调用currCb处理saga中的后续流程
// redux派发的action须匹配io.take方法返回值pattern时,将action作为interator.next(arg)的参数arg
_utils.is.notUndef(data = _io.asEffect.take(effect)) ? runTakeEffect(data, currCb) :
// effect为{ [IO]:true, PUT:{channel,action} }形式,io.asEffect.put获取effect的PUT属性{channel,action}
// 该情形在interator.next即saga.next中调用io.put方法时触发
// 意义是用于dispatch一个action,返回值可作为延迟对象或一般返回值交给currCb处理
_utils.is.notUndef(data = _io.asEffect.put(effect)) ? runPutEffect(data, currCb) :
// effect为{ [IO]:true, RACE:effects }形式,io.asEffect.race获取effect的RACE属性
// 该情形在interator.next即saga.next中调用io.race方法时触发
// 意义是多个延迟函数予以竞赛,io.race({effect1:fetch1,effect2:fetch2}),先执行完成的延迟函数构成返回值
// 如fetch1函数先执行完成,返回{effect1:res1,effect2:undefined},作为currCb的参数
_utils.is.notUndef(data = _io.asEffect.race(effect)) ? runRaceEffect(data, effectId, currCb) :
// effect为{ [IO]:true, CALL:{context,fn,args} },io.asEffect.call获取effect的CALL属性
// 该情形在saga中调用io.call或io.apply方法时触发,以上下文context执行CALL属性中的函数fn
// io.call([context,fn] | {context,fn} | fn,...args)与apply(context,fn,[args])设置上下文方式不同
// 意义是执行CALL属性中的函数fn,其返回值作为延迟对象或一般返回值交给currCb处理
_utils.is.notUndef(data = _io.asEffect.call(effect)) ? runCallEffect(data, effectId, currCb) :
// effect为{ [IO]:true, CPS:{context,fn,args} }形式,io.asEffect.cps获取effect的CPS属性
// 该情形在interator.next即saga.next中调用io.cps方法时触发,以上下文context执行CALL属性中的函数fn
// 意义是执行CPS属性中的函数fn,currCb作为尾参数,在函数fn中以回调函数方式执行;典型的fn函数为node端的readFile函数
_utils.is.notUndef(data = _io.asEffect.cps(effect)) ? runCPSEffect(data, currCb) :
// effect为{ [IO]:true, FORK:{context,fn,args} }形式,io.asEffect.fork获取effect的FORK属性
// 该情形在interator.next即saga.next中调用io.fork方法时触发,函数fn转化为迭代器后创建子task
// 意义是创建子task,由子task处理延迟的action事件;避免由saga处理延迟事件时,saga.next方法被挂起,外部无法调用
// io.fork执行后,saga.next接受的参数为通过函数fn构建的子task
// 注:io.fork的函数可接受普通函数、生成器函数或迭代器函数
_utils.is.notUndef(data = _io.asEffect.fork(effect)) ? runForkEffect(data, effectId, currCb) :
// effect为{ [IO]:true, JOIN:task }形式,io.asEffect.join获取effect的JOIN属性
// 该情形在interator.next即saga.next中调用io.join方法时触发,收集子task的返回值
// 意义是收集子task的返回值;或者当子task在整个生命周期中存活,则将主task添加到子task的joinners属性中
_utils.is.notUndef(data = _io.asEffect.join(effect)) ? runJoinEffect(data, currCb) :
// effect为{ [IO]:true, CANCEL:task }形式,io.asEffect.cancel获取effect的CANCEL属性
// 该情形在interator.next即saga.next中调用io.cancel方法时触发,终结主或子task
// 意义是终结主或子task的运作
_utils.is.notUndef(data = _io.asEffect.cancel(effect)) ? runCancelEffect(data, currCb) :
// effect为{ [IO]:true, SELECT:{selector,args} }形式,io.asEffect.select获取effect的SELECT属性
// 该情形在interator.next即saga.next中调用io.select方法时触发,由参数args、selector获取state,作为saga.next的参数
// 意义是由参数args、selector获取state,作为saga.next的参数;可用于特定的state时的日志操作
_utils.is.notUndef(data = _io.asEffect.select(effect)) ? runSelectEffect(data, currCb) :
// effect为{ [IO]:true, ACTION_CHANNEL:{pattern,buffer} }形式,io.asEffect.actionChannel获取effect的ACTION_CHANNEL属性
// 该情形在interator.next即saga.next中调用io.actionChannel方法时触发,意义派发某action时执行特定的订阅函数
// 意义是添加订阅函数,监听某action的执行,触发订阅函数的调用
_utils.is.notUndef(data = _io.asEffect.actionChannel(effect)) ? runChannelEffect(data, currCb) :
// effect为{ [IO]:true, FLUSH:channel }形式,io.asEffect.flush获取effect的FLUSH属性
// 该情形在interator.next即saga.next中调用io.flush法时触发,触发channel中订阅函数的执行,处理当前的action
_utils.is.notUndef(data = _io.asEffect.flush(effect)) ? runFlushEffect(data, currCb) :
// effect为{ [IO]:true, CANCELLED:{} }形式,io.asEffect.cancelled获取effect的CANCELLED属性
// 该情形在interator.next即saga.next中调用io.cancel方法时触发
// 意义是判断主task是否执行完结,saga.next获得参数为主task执行状态
_utils.is.notUndef(data = _io.asEffect.cancelled(effect)) ? runCancelledEffect(data, currCb) : /* anything else returned as is */
currCb(effect)
);
}
// intertor.next(arg)即saga.next(arg)返回promise对象时,将currCub作为promise的回调
// 参数cb为runEffect函数中的currCub,将调用proc下next流程控制函数
function resolvePromise(promise, cb) {
var cancelPromise = promise[_utils.CANCEL];
if (typeof cancelPromise === 'function') {
cb.cancel = cancelPromise;
}
promise.then(cb, function (error) {
return cb(error, true);
});
}
// intertor.next(arg)即saga.next(arg)返回迭代器对象时,调用proc构建新的子task
// 参数cb为runEffect函数中的currCub,将调用proc下next流程控制函数
function resolveIterator(iterator, effectId, name, cb) {
proc(iterator, subscribe, dispatch, getState, options, effectId, name, cb);
}
// runTakeEffect函数在saga中调用io.take方法时触发调用,处理io.take方法返回值的TAKE属性
// 也可能当saga为sagaHelpers.takeEveryHelper|takeLatestHelper|throttleHelper构建的迭代器时触发
// 意义是将takecb存入stdChannel中,redux机制派发action时通过sagaEmitter.emit传入action,调用takecb
// takeCb执行过程中,执行saga后续流程
// 参数_ref2为io.take方法返回值的TAKE属性{channel}或{pattern}
// 参数cb为runEffect函数中的currCub,将调用proc下next流程控制函数
function runTakeEffect(_ref2, cb) {
var channel = _ref2.channel,
pattern = _ref2.pattern,
maybe = _ref2.maybe;
channel = channel || stdChannel;
var takeCb = function takeCb(inp) {
return inp instanceof Error ? cb(inp, true) :
(0, _channel.isEnd)(inp) && !maybe ? cb(CHANNEL_END) : cb(inp);
};
try {
// 将takecb存入stdChannel中,redux机制派发action时通过sagaEmitter.emit传入action,调用takecb
// takeCb的'@@redux-saga/MATCH'属性置为次参matcher(pattern)
channel.take(takeCb, matcher(pattern));
} catch (err) {
return cb(err, true);
}
cb.cancel = takeCb.cancel;
}
// runPutEffect函数在saga中调用io.put方法时触发调用,处理io.put方法返回值的PUT属性
// 意义是在saga中触发redux机制派发action的动作,返回值以延迟函数或普通返回值处理
// 参数_ref3为io.put方法返回值的Put属性{channel,action}
// 参数cb为runEffect函数中的currCub,将调用proc下next流程控制函数
function runPutEffect(_ref3, cb) {
var channel = _ref3.channel,
action = _ref3.action,
resolve = _ref3.resolve;
(0, _scheduler.asap)(function () {
var result = void 0;
try {
result = (channel ? channel.put : dispatch)(action);
} catch (error) {
if (channel || resolve) return cb(error, true);
log('error', 'uncaught at ' + name, error.stack || error.message || error);
}
if (resolve && _utils.is.promise(result)) {
resolvePromise(result, cb);
} else {
return cb(result);
}
});
}
// runCallEffect函数在saga中调用io.call或io.apply方法时触发调用,处理io.call或或io.apply方法返回值的CALL属性
// 参数_ref4为io.call或io.apply方法返回值的CALL属性{context,fn,args},执行fn函数
// 参数cb为runEffect函数中的currCub,将调用proc下next流程控制函数
function runCallEffect(_ref4, effectId, cb) {
var context = _ref4.context,
fn = _ref4.fn,
args = _ref4.args;
var result = void 0;
try {
result = fn.apply(context, args);
} catch (error) {
return cb(error, true);
}
return _utils.is.promise(result) ? resolvePromise(result, cb) :
_utils.is.iterator(result) ? resolveIterator(result, effectId, fn.name, cb) : cb(result);
}
// runCPSEffect函数在saga中调用io.cps方法时触发调用,处理io.cps方法返回值的CPS属性
// 参数_ref5为io.cps方法返回值的CPS属性{context,fn,args},执行fn函数,并将currCub作为函数fn的尾参回调函数
// 典型的fn函数如node的readFile函数,执行过程中自动调用尾参回调函数处理错误或成功值
// 参数cb为runEffect函数中的currCub,将调用proc下next流程控制函数
function runCPSEffect(_ref5, cb) {
var context = _ref5.context,
fn = _ref5.fn,
args = _ref5.args;
try {
var cpsCb = function cpsCb(err, res) {
return _utils.is.undef(err) ? cb(res) : cb(err, true);
};
fn.apply(context, args.concat(cpsCb));
if (cpsCb.cancel) {
cb.cancel = function () {
return cpsCb.cancel();
};
}
} catch (error) {
return cb(error, true);
}
}
// 针对异常情况:点击某按钮触发fetchApi延迟函数,最后更新按钮loading状态
// watchFetch通过sagaHelper挂载为saga时,一次点击延迟过程中,再次点击无法调用redux-saga机制处理FETCH_POSTS
// function* watchFetch() {
// while ( yield take(FETCH_POSTS) ) {
// yield put( actions.requestPosts() )
// const posts = yield call(fetchApi, '/posts') // blocking call
// yield put( actions.receivePosts(posts) )
// }
// }
// io.fork方法处理前述异常情况,在saga.next方法执行时创建子task,由子task处理FETCH_POSTS
// 以避免saga.next在延迟函数执行过程中挂起,外部无法调用
// 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
// }
// }
// runForkEffect函数在saga中调用io.fork方法时触发调用,处理io.fork方法返回值的FORK属性
// 参数_ref4为io.fork方法返回值的FORK属性{context,fn,args},通过fn构建迭代器函数,创建子task
// 通过middleware.run导入的saga.next执行过程中,外部无法触发currCub的执行,同样的action无法触发redux-saga的处理机制
// 这种情况下,在saga.next的执行过程中创建子task,由子task处理同名且为延迟事件的action
// 参数cb为runEffect函数中的currCub,将调用proc下next流程控制函数
function runForkEffect(_ref6, effectId, cb) {
var context = _ref6.context,
fn = _ref6.fn,
args = _ref6.args,
detached = _ref6.detached;
var taskIterator = createTaskIterator({ context: context, fn: fn, args: args });
try {
(0, _scheduler.suspend)();
var _task = proc(taskIterator, subscribe, dispatch, getState, options, effectId, fn.name, detached ? null : _utils.noop);
if (detached) {
cb(_task);
} else {
if (taskIterator._isRunning) {
taskQueue.addTask(_task);
cb(_task);
} else if (taskIterator._error) {
taskQueue.abort(taskIterator._error);
} else {
cb(_task);
}
}
} finally {
(0, _scheduler.flush)();
}
}
// 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)
// }
// runJoinEffect函数在saga中调用io.join方法时触发调用,处理io.join方法返回值的TASK属性
// 参数t为io.join方法或返回值的TASK属性{task},即子task,主要目的是获取子task的执行结果
// 参数cb为runEffect函数中的currCub,将调用proc下next流程控制函数
function runJoinEffect(t, cb) {
// t.isRunning()为真,意味子task在整个生命周期中都存活,将主task添加到子task的joinners属性中
if (t.isRunning()) {
var joiner = { task: task, cb: cb };
cb.cancel = function () {
return (0, _utils.remove)(t.joiners, joiner);
};
t.joiners.push(joiner);
// 否则收集子task的返回值或错误
} else {
t.isAborted() ? cb(t.error(), true) : cb(t.result());
}
}
// runCancelEffect函数在saga中调用io.cancel方法时触发调用,处理io.cancel方法返回值的CANCEL属性
// 参数t为io.cancel方法或返回值的CANCEL属性{task},即主或子task,主要目的是终结主或子task
// 参数cb为runEffect函数中的currCub,将调用proc下next流程控制函数
function runCancelEffect(taskToCancel, cb) {
if (taskToCancel === _utils.SELF_CANCELLATION) {
taskToCancel = task;
}
if (taskToCancel.isRunning()) {
taskToCancel.cancel();
}
cb();
}
// interator.next即saga.next方法执行时返回数组,对数组中每个子项迭代调用runEffect函数
// 参数cb为runEffect函数中的currCub,将调用proc下next流程控制函数,获得的参数为由每个effect处理值构建的数组
function runParallelEffect(effects, effectId, cb) {
if (!effects.length) {
return cb([]);
}
var completedCount = 0;
var completed = void 0;
var results = Array(effects.length);
function checkEffectEnd() {
if (completedCount === results.length) {
completed = true;
cb(results);
}
}
var childCbs = effects.map(function (eff, idx) {
var chCbAtIdx = function chCbAtIdx(res, isErr) {
if (completed) {
return;
}
if (isErr || (0, _channel.isEnd)(res) || res === CHANNEL_END || res === TASK_CANCEL) {
cb.cancel();
cb(res, isErr);
} else {
results[idx] = res;
completedCount++;
checkEffectEnd();
}
};
chCbAtIdx.cancel = _utils.noop;
return chCbAtIdx;
});
cb.cancel = function () {
if (!completed) {
completed = true;
childCbs.forEach(function (chCb) {
return chCb.cancel();
});
}
};
effects.forEach(function (eff, idx) {
return runEffect(eff, effectId, idx, childCbs[idx]);
});
}
// const {posts, timeout} = yield race({
// posts : call(fetchApi, '/posts'),
// timeout : call(delay, 1000)
// })
// if(posts)
// put( actions.receivePosts(posts) )
// else
// put( actions.timeoutError() )
// runForkEffect函数在saga中调用io.race方法时触发调用,处理io.race方法返回值的RACE属性{effect:fetch(){}}
// 参数effects为io.race方法返回值的RACE属性{effect1:fetch1(){},effect2:fetch2(){}},执行延迟函数fetch1、fetch2
// 先执行完成的延迟函数以对象构成返回值,若fetch1先执行完成,则返回{effect1:res1,effect2:undefined}
// 返回值作为currCub的参数,并执行currCub,再次调用proc下next流程控制函数
// 参数cb为runEffect函数中的currCub,将调用proc下next流程控制函数
function runRaceEffect(effects, effectId, cb) {
var completed = void 0;
var keys = Object.keys(effects);
var childCbs = {};
keys.forEach(function (key) {
var chCbAtKey = function chCbAtKey(res, isErr) {
if (completed) {
return;
}
if (isErr) {
cb.cancel();
cb(res, true);
} else if (!(0, _channel.isEnd)(res) && res !== CHANNEL_END && res !== TASK_CANCEL) {
cb.cancel();
completed = true;
cb(_defineProperty({}, key, res));
}
};
chCbAtKey.cancel = _utils.noop;
childCbs[key] = chCbAtKey;
});
cb.cancel = function () {
// prevents unnecessary cancellation
if (!completed) {
completed = true;
keys.forEach(function (key) {
return childCbs[key].cancel();
});
}
};
keys.forEach(function (key) {
if (completed) {
return;
}
runEffect(effects[key], effectId, key, childCbs[key]);
});
}
// runCancelEffect函数在saga中调用io.select方法时触发调用,处理io.select方法返回值的SELECT属性
// 参数t为io.select方法或返回值的SELECT属性{selector,args},由参数args、selector获取state,作为saga.next的参数
// 参数cb为runEffect函数中的currCub,将调用proc下next流程控制函数
function runSelectEffect(_ref7, cb) {
var selector = _ref7.selector,
args = _ref7.args;
try {
var state = selector.apply(undefined, [getState()].concat(_toConsumableArray(args)));
cb(state);
} catch (error) {
cb(error, true);
}
}
// runChannelEffect函数在saga中调用io.actionChannel方法时触发调用,处理io.actionChannel方法返回值的ACTION_CHANNEL属性
// 参数t为io.actionChannel方法或返回值的ACTION_CHANNEL属性{pattern,buffer}
// pattern作为action的筛选条件,buffer作为channel.eventChannel的缓存
// 意义是添加某action派发时触发执行channel.eventChannel下的订阅函数
// 参数cb为runEffect函数中的currCub,将调用proc下next流程控制函数
function runChannelEffect(_ref8, cb) {
var pattern = _ref8.pattern,
buffer = _ref8.buffer;
var match = matcher(pattern);// 构建匹配函数
match.pattern = pattern;
cb((0, _channel.eventChannel)(subscribe, buffer || _buffers.buffers.fixed(), match));
}
function runCancelledEffect(data, cb) {
cb(!!mainTask.isCancelled);
}
// 触发channel中订阅函数的执行
function runFlushEffect(channel, cb) {
channel.flush(cb);
}
// 返回
// {
// '@@redux-saga/TASK': true,
// id: id,
// name: name,
// cont: cont,
// joiners: [],
// cancel: cancel,
// isRunning: ()=>iterator._isRunning,
// isCancelled: ()=>iterator._isCancelled,
// isAborted: ()=>iterator._isAborted,
// result: ()=>iterator._result,
// error: ()=>iterator._error,
// get done 属性修饰符约定只能获取,返回promise对象
// }
function newTask(id, name, iterator, cont) {
var _done, _ref9, _mutatorMap;
iterator._deferredEnd = null;
return _ref9 = {},
_defineProperty(_ref9, _utils.TASK, true),// utils.TASK 即常量'@@redux-saga/TASK'
_defineProperty(_ref9, 'id', id),
_defineProperty(_ref9, 'name', name),
_done = 'done',
_mutatorMap = {},
_mutatorMap[_done] = _mutatorMap[_done] || {},
_mutatorMap[_done].get = function () {
if (iterator._deferredEnd) {
return iterator._deferredEnd.promise;
} else {
// utils.deferred(props) 构建promise对象,并返回{promise,resolve,reject,...props}
var def = (0, _utils.deferred)();
iterator._deferredEnd = def;
if (!iterator._isRunning) {
iterator._error ? def.reject(iterator._error) : def.resolve(iterator._result);
}
return def.promise;
}
},
_defineProperty(_ref9, 'cont', cont),
_defineProperty(_ref9, 'joiners', []),
_defineProperty(_ref9, 'cancel', cancel),
_defineProperty(_ref9, 'isRunning', function isRunning() {
return iterator._isRunning;
}),
_defineProperty(_ref9, 'isCancelled', function isCancelled() {
return iterator._isCancelled;
}),
_defineProperty(_ref9, 'isAborted', function isAborted() {
return iterator._isAborted;
}),
_defineProperty(_ref9, 'result', function result() {
return iterator._result;
}),
_defineProperty(_ref9, 'error', function error() {
return iterator._error;
}),
_defineEnumerableProperties(_ref9, _mutatorMap),
_ref9;
}
}
io.js将saga.next返回值分类,以设置处理策略
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.asEffect = exports.takem = undefined;
var _slicedToArray = function () {
// 参数arr若为数组,获取长度为i的该数组备份;若为对象且可迭代,获取长度为i的迭代内容,构成数组返回
function sliceIterator(arr, i) {
var _arr = [];
var _n = true;
var _d = false;
var _e = undefined;
try {
for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) {
_arr.push(_s.value);
if (i && _arr.length === i) break;
}
} catch (err) {
_d = true;
_e = err;
} finally {
try {
if (!_n && _i["return"]) _i["return"]();
} finally {
if (_d) throw _e;
}
}
return _arr;
}
return function (arr, i) {
if (Array.isArray(arr)) {
return arr;
} else if (Symbol.iterator in Object(arr)) {
return sliceIterator(arr, i);
} else {
throw new TypeError("Invalid attempt to destructure non-iterable instance");
}
};
}();
exports.take = take;
exports.put = put;
exports.race = race;
exports.call = call;
exports.apply = apply;
exports.cps = cps;
exports.fork = fork;
exports.spawn = spawn;
exports.join = join;
exports.cancel = cancel;
exports.select = select;
exports.actionChannel = actionChannel;
exports.cancelled = cancelled;
exports.flush = flush;
exports.takeEvery = takeEvery;
exports.takeLatest = takeLatest;
exports.throttle = throttle;
var _utils = require('./utils');
var _sagaHelpers = require('./sagaHelpers');
// 设置obj对象的key属性为value,obj对象原有key属性,则key属性可枚举、可配置、可改写
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 IO = (0, _utils.sym)('IO');
var TAKE = 'TAKE';
var PUT = 'PUT';
var RACE = 'RACE';
var CALL = 'CALL';
var CPS = 'CPS';
var FORK = 'FORK';
var JOIN = 'JOIN';
var CANCEL = 'CANCEL';
var SELECT = 'SELECT';
var ACTION_CHANNEL = 'ACTION_CHANNEL';
var CANCELLED = 'CANCELLED';
var FLUSH = 'FLUSH';
var TEST_HINT = '\n(HINT: if you are getting this errors in tests,'
+ ' consider using createMockTask from redux-saga/utils)';
// 方法deprecated更迭为方法preferred,提示
var deprecationWarning = function deprecationWarning(deprecated, preferred) {
return deprecated + ' has been deprecated in favor of ' + preferred + ', please update your code';
};
// effect(type,payload) 返回{ [IO]:true, [type]:payload }
var effect = function effect(type, payload) {
var _ref;
return _ref = {}, _defineProperty(_ref, IO, true), _defineProperty(_ref, type, payload), _ref;
};
// take(patternOrChannel) 返回{ [IO]:true, TAKE:{pattern} }或{ [IO]:true, TAKE:{channel} }
// pattern为字符串、symbol类型、函数、或数组;channel为channel模块下各类channel={take,flush,close}
// patternOrChannel接受字符串、symbol类型、函数、数组、或channel模块下各类channel={take,flush,close}
function take() {
var patternOrChannel = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : '*';
if (arguments.length) {
(0, _utils.check)(arguments[0], _utils.is.notUndef,
'take(patternOrChannel): patternOrChannel is undefined');
}
if (_utils.is.pattern(patternOrChannel)) {
// effect(type,payload) 返回{ [IO]:true, [type]:payload }
return effect(TAKE, { pattern: patternOrChannel });
}
if (_utils.is.channel(patternOrChannel)) {
// effect(type,payload) 返回{ [IO]:true, [type]:payload }
return effect(TAKE, { channel: patternOrChannel });
}
throw new Error('take(patternOrChannel): argument ' + String(patternOrChannel)
+ ' is not valid channel or a valid pattern');
}
// 返回{ [IO]:true, TAKE:{pattern,maybe:true} }或{ [IO]:true, TAKE:{channel,maybe:true} }
take.maybe = function () {
var eff = take.apply(undefined, arguments);
eff[TAKE].maybe = true;
return eff;
};
var takem = exports.takem = (0, _utils.deprecate)(take.maybe, deprecationWarning('takem', 'take.maybe'));
// put([channel],action) 返回{ [IO]:true, PUT:{channel,action} }
function put(channel, action) {
if (arguments.length > 1) {
(0, _utils.check)(channel, _utils.is.notUndef, 'put(channel, action): argument channel is undefined');
(0, _utils.check)(channel, _utils.is.channel,
'put(channel, action): argument ' + channel + ' is not a valid channel');
(0, _utils.check)(action, _utils.is.notUndef, 'put(channel, action): argument action is undefined');
} else {
(0, _utils.check)(channel, _utils.is.notUndef, 'put(action): argument action is undefined');
action = channel;
channel = null;
}
// effect(type,payload) 返回{ [IO]:true, [type]:payload }
return effect(PUT, { channel: channel, action: action });
}
// put([channel],action) 返回{ [IO]:true, PUT:{channel,action,resolve:true} }
put.resolve = function () {
var eff = put.apply(undefined, arguments);
eff[PUT].resolve = true;
return eff;
};
put.sync = (0, _utils.deprecate)(put.resolve, deprecationWarning('put.sync', 'put.resolve'));
// race(effects) 返回{ [IO]:true, RACE:effects }
function race(effects) {
// effect(type,payload) 返回{ [IO]:true, [type]:payload }
return effect(RACE, effects);
}
// getFnCallDesc(meth, [context,fn] | {context,fn} | fn, args) 返回{context,fn,args}
function getFnCallDesc(meth, fn, args) {
(0, _utils.check)(fn, _utils.is.notUndef, meth + ': argument fn is undefined');
var context = null;
if (_utils.is.array(fn)) {
var _fn = fn;
var _fn2 = _slicedToArray(_fn, 2);
context = _fn2[0];
fn = _fn2[1];
} else if (fn.fn) {
var _fn3 = fn;
context = _fn3.context;
fn = _fn3.fn;
}
(0, _utils.check)(fn, _utils.is.func, meth + ': argument ' + fn + ' is not a function');
return { context: context, fn: fn, args: args };
}
// call([context,fn] | {context,fn} | fn,...args) 返回{ [IO]:true, CALL:{context,fn,args} }
function call(fn) {
for (var _len = arguments.length, args = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
args[_key - 1] = arguments[_key];
}
// getFnCallDesc(meth, [context,fn] | {context,fn} | fn, args) 返回{context,fn,args}
// effect(type,payload) 返回{ [IO]:true, [type]:payload }
return effect(CALL, getFnCallDesc('call', fn, args));
}
// apply(context,fn,[args]) 返回{ [IO]:true, CALL:{context,fn,args} }
function apply(context, fn) {
var args = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : [];
// getFnCallDesc(meth, [context,fn] | {context,fn} | fn, args) 返回{context,fn,args}
// effect(type,payload) 返回{ [IO]:true, [type]:payload }
return effect(CALL, getFnCallDesc('apply', { context: context, fn: fn }, args));
}
// cps([context,fn] | {context,fn} | fn,...args) 返回{ [IO]:true, CPS:{context,fn,args} }
function cps(fn) {
for (var _len2 = arguments.length, args = Array(_len2 > 1 ? _len2 - 1 : 0), _key2 = 1; _key2 < _len2; _key2++) {
args[_key2 - 1] = arguments[_key2];
}
// getFnCallDesc(meth, [context,fn] | {context,fn} | fn, args) 返回{context,fn,args}
// effect(type,payload) 返回{ [IO]:true, [type]:payload }
return effect(CPS, getFnCallDesc('cps', fn, args));
}
// fork([context,fn] | {context,fn} | fn,...args) 返回{ [IO]:true, FORK:{context,fn,args} }
function fork(fn) {
for (var _len3 = arguments.length, args = Array(_len3 > 1 ? _len3 - 1 : 0), _key3 = 1; _key3 < _len3; _key3++) {
args[_key3 - 1] = arguments[_key3];
}
// getFnCallDesc(meth, [context,fn] | {context,fn} | fn, args) 返回{context,fn,args}
// effect(type,payload) 返回{ [IO]:true, [type]:payload }
return effect(FORK, getFnCallDesc('fork', fn, args));
}
// spawn([context,fn] | {context,fn} | fn,...args) 返回{ [IO]:true, FORK:{context,fn,args,detached:true} }
function spawn(fn) {
for (var _len4 = arguments.length, args = Array(_len4 > 1 ? _len4 - 1 : 0), _key4 = 1; _key4 < _len4; _key4++) {
args[_key4 - 1] = arguments[_key4];
}
// fork([context,fn] | {context,fn} | fn,...args) 返回{ [IO]:true, FORK:{context,fn,args} }
var eff = fork.apply(undefined, [fn].concat(args));
eff[FORK].detached = true;
return eff;
}
// join(...tasks) 若tasks为多项,返回[{ [IO]:true, JOIN:task }];只有一项,返回{ [IO]:true, JOIN:task }
function join() {
for (var _len5 = arguments.length, tasks = Array(_len5), _key5 = 0; _key5 < _len5; _key5++) {
tasks[_key5] = arguments[_key5];
}
if (tasks.length > 1) {
return tasks.map(function (t) {
return join(t);
});
}
var task = tasks[0];
(0, _utils.check)(task, _utils.is.notUndef, 'join(task): argument task is undefined');
(0, _utils.check)(task, _utils.is.task, 'join(task): argument ' + task
+ ' is not a valid Task object ' + TEST_HINT);
// effect(type,payload) 返回{ [IO]:true, [type]:payload }
return effect(JOIN, task);
}
// cancel(...tasks) 若tasks为多项,返回[{ [IO]:true, CANCEL:task }];只有一项,返回{ [IO]:true, CANCEL:task }
function cancel() {
for (var _len6 = arguments.length, tasks = Array(_len6), _key6 = 0; _key6 < _len6; _key6++) {
tasks[_key6] = arguments[_key6];
}
if (tasks.length > 1) {
return tasks.map(function (t) {
return cancel(t);
});
}
var task = tasks[0];
if (tasks.length === 1) {
(0, _utils.check)(task, _utils.is.notUndef, 'cancel(task): argument task is undefined');
(0, _utils.check)(task, _utils.is.task, 'cancel(task): argument ' + task
+ ' is not a valid Task object ' + TEST_HINT);
}
// effect(type,payload) 返回{ [IO]:true, [type]:payload }
return effect(CANCEL, task || _utils.SELF_CANCELLATION);// _utils.SELF_CANCELLATION为'@@redux-saga/SELF_CANCELLATION'
}
// select(selector,...args) 返回{ [IO]:true, SELECT:{selector,args} }
function select(selector) {
for (var _len7 = arguments.length, args = Array(_len7 > 1 ? _len7 - 1 : 0), _key7 = 1; _key7 < _len7; _key7++) {
args[_key7 - 1] = arguments[_key7];
}
if (arguments.length === 0) {
selector = _utils.ident;// _utils.ident为function ident(v) {return v;};
} else {
(0, _utils.check)(selector, _utils.is.notUndef, 'select(selector,[...]): argument selector is undefined');
(0, _utils.check)(selector, _utils.is.func, 'select(selector,[...]): argument ' + selector
+ ' is not a function');
}
// effect(type,payload) 返回{ [IO]:true, [type]:payload }
return effect(SELECT, { selector: selector, args: args });
}
// actionChannel(pattern,[buffer]) 返回{ [IO]:true, ACTION_CHANNEL:{pattern,buffer} }
function actionChannel(pattern, buffer) {
(0, _utils.check)(pattern, _utils.is.notUndef, 'actionChannel(pattern,...): argument pattern is undefined');
if (arguments.length > 1) {
(0, _utils.check)(buffer, _utils.is.notUndef, 'actionChannel(pattern, buffer): argument buffer is undefined');
(0, _utils.check)(buffer, _utils.is.buffer, 'actionChannel(pattern, buffer): argument ' + buffer + ' is not a valid buffer');
}
// effect(type,payload) 返回{ [IO]:true, [type]:payload }
return effect(ACTION_CHANNEL, { pattern: pattern, buffer: buffer });
}
// 返回{ [IO]:true, CANCELLED:{} }
function cancelled() {
// effect(type,payload) 返回{ [IO]:true, [type]:payload }
return effect(CANCELLED, {});
}
// flush(channel) 返回{ [IO]:true, FLUSH:channel }
function flush(channel) {
(0, _utils.check)(channel, _utils.is.channel, 'flush(channel): argument ' + channel + ' is not valid channel');
// effect(type,payload) 返回{ [IO]:true, [type]:payload }
return effect(FLUSH, channel);
}
// takeEvery(patternOrChannel,worker,...args)
// 返回
// {
// [IO]:true,
// FORK:{context:undefined,fn:_sagaHelpers.takeEveryHelper,args:[patternOrChannel,worker,...args]}
// }
function takeEvery(patternOrChannel, worker) {
for (var _len8 = arguments.length, args = Array(_len8 > 2 ? _len8 - 2 : 0), _key8 = 2; _key8 < _len8; _key8++) {
args[_key8 - 2] = arguments[_key8];
}
// fork([context,fn] | {context,fn} | fn,...args) 返回{ [IO]:true, FORK:{context,fn,args} }
return fork.apply(undefined, [_sagaHelpers.takeEveryHelper, patternOrChannel, worker].concat(args));
}
// takeEvery(patternOrChannel,worker,...args)
// 返回
// {
// [IO]:true,
// FORK:{context:undefined,fn:_sagaHelpers.takeLatestHelper,args:[patternOrChannel,worker,...args]}
// }
function takeLatest(patternOrChannel, worker) {
for (var _len9 = arguments.length, args = Array(_len9 > 2 ? _len9 - 2 : 0), _key9 = 2; _key9 < _len9; _key9++) {
args[_key9 - 2] = arguments[_key9];
}
// fork([context,fn] | {context,fn} | fn,...args) 返回{ [IO]:true, FORK:{context,fn,args} }
return fork.apply(undefined, [_sagaHelpers.takeLatestHelper, patternOrChannel, worker].concat(args));
}
// takeEvery(patternOrChannel,worker,...args)
// 返回
// {
// [IO]:true,
// FORK:{context:undefined,fn:_sagaHelpers.throttleHelper,args:[patternOrChannel,worker,...args]}
// }
function throttle(ms, pattern, worker) {
for (var _len10 = arguments.length, args = Array(_len10 > 3 ? _len10 - 3 : 0), _key10 = 3; _key10 < _len10; _key10++) {
args[_key10 - 3] = arguments[_key10];
}
// fork([context,fn] | {context,fn} | fn,...args) 返回{ [IO]:true, FORK:{context,fn,args} }
return fork.apply(undefined, [_sagaHelpers.throttleHelper, ms, pattern, worker].concat(args));
}
// 返回函数获取effect的TAKE、PUT、RACE、CALL、CPS、FORK、JOIN、CANCEL、SELECT、ACTION_CHANNEL、CANCELLED、或FLUSH属性
var createAsEffectType = function createAsEffectType(type) {
return function (effect) {
return effect && effect[IO] && effect[type];
};
};
var asEffect = exports.asEffect = {
take: createAsEffectType(TAKE),
put: createAsEffectType(PUT),
race: createAsEffectType(RACE),
call: createAsEffectType(CALL),
cps: createAsEffectType(CPS),
fork: createAsEffectType(FORK),
join: createAsEffectType(JOIN),
cancel: createAsEffectType(CANCEL),
select: createAsEffectType(SELECT),
actionChannel: createAsEffectType(ACTION_CHANNEL),
cancelled: createAsEffectType(CANCELLED),
flush: createAsEffectType(FLUSH)
};
channel.js事件机制支持
emitter用于添加绑定函数或触发函数执行
channel用于存储多个绑定函数,并以特定参数执行
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.UNDEFINED_INPUT_ERROR = exports.INVALID_BUFFER = exports.isEnd = exports.END = undefined;
// 浅拷贝
var _extends = Object.assign || function (target) {
for (var i = 1; i < arguments.length; i++) {
var source = arguments[i];
for (var key in source) {
if (Object.prototype.hasOwnProperty.call(source, key)) {
target[key] = source[key];
}
}
}
return target;
};
exports.emitter = emitter;
exports.channel = channel;
exports.eventChannel = eventChannel;
exports.stdChannel = stdChannel;
var _utils = require('./utils');
var _buffers = require('./buffers');
var _scheduler = require('./scheduler');
var CHANNEL_END_TYPE = '@@redux-saga/CHANNEL_END';
var END = exports.END = { type: CHANNEL_END_TYPE };
var isEnd = exports.isEnd = function isEnd(a) {
return a && a.type === CHANNEL_END_TYPE;
};
// 添加订阅函数,或执行订阅函数
function emitter() {
var subscribers = [];
// 向subscribers添加sub订阅函数,返回函数用于移除subscribers中添加的订阅函数
function subscribe(sub) {
subscribers.push(sub);
return function () {
return (0, _utils.remove)(subscribers, sub);
};
}
// 以参数item执行subscribers中存储的各订阅函数
function emit(item) {
var arr = subscribers.slice();
for (var i = 0, len = arr.length; i < len; i++) {
arr[i](item);
}
}
return {
subscribe: subscribe,
emit: emit
};
}
// 无效的buffer缓存
var INVALID_BUFFER = exports.INVALID_BUFFER = 'invalid buffer passed to channel factory function';
// 无效的action
var UNDEFINED_INPUT_ERROR = exports.UNDEFINED_INPUT_ERROR = 'Saga was provided with an undefined action';
if (process.env.NODE_ENV !== 'production') {
exports.UNDEFINED_INPUT_ERROR = UNDEFINED_INPUT_ERROR +=
'\nHints:\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 ';
}
// 返回{take,put,flush,close,__takers__,__closed__}
// take(cb) buffer参数缓存有值时,执行cb函数;或将cb存入takers获取者函数缓存
// put(action) takers获取者函数缓存有值时,以action为参数执行各获取者函数;或将action存入buffer参数缓存
// flush(cb) buffer参数缓存有值时,执行cb函数;或执行cb({type:'@@redux-saga/CHANNEL_END'})
// __takers__ 获取takers缓存
// __closed__ 判断close方法是否已执行,channel频道已关闭
function channel() {
// 首参缓存(通过buffers模块构建或自行构建含isEmpty、put、take方法的缓存),或取buffers模块下固定长度的数组缓存
// buffer缓存用于存储takers缓存中的各获取者函数的参数,即redux机制下的action
var buffer = arguments.length > 0 && arguments[0] !== undefined ?
arguments[0] : _buffers.buffers.fixed();
var closed = false;// 频道channel关闭标识符,close方法执行时置为真值
var takers = [];
// 通过isEmpty、put、take方法校验buffer缓存是否buffers模块的数据结构相同
(0, _utils.check)(buffer, _utils.is.buffer, INVALID_BUFFER);
function checkForbiddenStates() {
// close方法执行关闭频道channel后,不能再使用take方法添加taker获取者函数,也判断close是否清空takers缓存
if (closed && takers.length) {
throw (0, _utils.internalErr)('Cannot have a closed channel with pending takers');
}
// takers缓存中有获取者函数,且buffer缓存含有获取者函数的参数,报错;即takers和buffer缓存不能同时有值
if (takers.length && !buffer.isEmpty()) {
throw (0, _utils.internalErr)('Cannot have pending takers with non empty buffer');
}
}
// 若takers缓存中无获取者函数时,将input压入buffer参数列表中;input即redux机制下的action
// 若takers缓存中有获取者函数时,取出各获取者函数并以参数input执行,最终takers将清空
function put(input) {
checkForbiddenStates();
(0, _utils.check)(input, _utils.is.notUndef, UNDEFINED_INPUT_ERROR);
if (closed) {
return;
}
if (!takers.length) {
return buffer.put(input);
}
for (var i = 0; i < takers.length; i++) {
var cb = takers[i];
if (!cb[_utils.MATCH] || cb[_utils.MATCH](input)) {
takers.splice(i, 1);
return cb(input);
}
}
}
// 若buffer参数缓存中没有action时,以参数{type:'@@redux-saga/CHANNEL_END'}执行cb
// 若buffer参数缓存中存储有action时,取出作为cb的参数,并执行cb
// 若closed为否,且buffer缓存中没有action时,将cb压入takers获取者函数列表中
function take(cb) {
checkForbiddenStates();
(0, _utils.check)(cb, _utils.is.func, 'channel.take\'s callback must be a function');
if (closed && buffer.isEmpty()) {
cb(END);
} else if (!buffer.isEmpty()) {
cb(buffer.take());
} else {
takers.push(cb);
cb.cancel = function () {
return (0, _utils.remove)(takers, cb);
};
}
}
// 将buffer参数缓存以数组形式作为cb的参数,并执行cb函数
function flush(cb) {
checkForbiddenStates(); // TODO: check if some new state should be forbidden now
(0, _utils.check)(cb, _utils.is.func, 'channel.flush\' callback must be a function');
if (closed && buffer.isEmpty()) {
cb(END);
return;
}
cb(buffer.flush());
}
// 取出takers缓存中的各获取者函数,以参数{type:'@@redux-saga/CHANNEL_END'}逐个执行
function close() {
checkForbiddenStates();
if (!closed) {
closed = true;
if (takers.length) {
var arr = takers;
takers = [];
for (var i = 0, len = arr.length; i < len; i++) {
arr[i](END);
}
}
}
}
return { take: take, put: put, flush: flush, close: close,
get __takers__() {
return takers;
},
get __closed__() {
return closed;
}
};
}
// eventChannel(subscribe,[buffer],[matcher]),返回{take,flush,close}
// 参数subscribe为let emitter = emitter();emitter.subscribe用于添加订阅函数,emitter.emit方法执行订阅函数
// 参数buffer,可以不传,作为chan的buffer参数缓存,默认取buffers模块的none方法返回值,input不作缓存
// 默认返回值take方法缓存taker获取者函数,flush方法无操作,emitter.emit方法间接执行takers缓存的各获取者函数
// 参数matcher,可以不传,筛选emitter.subscribe接受的input即redux机制下的action={type:""}
function eventChannel(subscribe) {
var buffer = arguments.length > 1 && arguments[1] !== undefined ?
arguments[1] : _buffers.buffers.none();
var matcher = arguments[2];
if (arguments.length > 2) {
(0, _utils.check)(matcher, _utils.is.func, 'Invalid match function passed to eventChannel');
}
var chan = channel(buffer);
// emitter.emit方法执行订阅者函数,获取chan的takers缓存获取者函数,以input为参数,并执行
// 或将input存入chan的buffer参数缓存中
// 参数input即redux机制下的action,matcher存在用于过滤input
var unsubscribe = subscribe(function (input) {
if (isEnd(input)) {
chan.close();
return;
}
if (matcher && !matcher(input)) {
return;
}
// channel函数中takers缓存有获取者函数时,直接将input作为参数执行各获取者函数
// 若没有,缓存到channel函数的buffer缓存中
chan.put(input);
});
if (!_utils.is.func(unsubscribe)) {
throw new Error('in eventChannel: subscribe should return a function to unsubscribe');
}
return {
// take(cb) 向chan添加获取者函数taker;或者调用参数cb函数以chan中的buffer缓存为参数,并执行
take: chan.take,
// flush(cb) 调用参数cb函数以chan中的buffer缓存为参数,并执行
// buffer缓存无内容,以参数{type:'@@redux-saga/CHANNEL_END'}执行cb
flush: chan.flush,
// 关闭chan,并清空订阅者函数
close: function close() {
if (!chan.__closed__) {
chan.close();
unsubscribe();
}
}
};
}
// stdChannel(subscribe) 返回{take,flush,close}
// take(cb,matcher)方法缓存taker获取者函数cb,并向cb添加'@@redux-saga/MATCH'属性为次参matcher
// flush方法无操作,close方法关闭chan,并清空订阅者函数
function stdChannel(subscribe) {
// chan={take,flush,close}
// take方法缓存taker获取者函数,flush方法无操作
// emitter.emit方法在订阅者函数的回调cb执行过程中,间接执行takers缓存的各获取者函数
var chan = eventChannel(function (cb) {
return subscribe(function (input) {
if (input[_utils.SAGA_ACTION]) {// _utils.SAGA_ACTION即'@@redux-saga/SAGA_ACTION'
cb(input);
return;
}
// scheduler.asap(task) 若semaphore为0,执行task任务函数及scheduler模块下queue缓存的所有任务函数
// 不为0,将task推入scheduler模块下queue缓存中
(0, _scheduler.asap)(function () {
return cb(input);
});
});
});
return _extends({}, chan, {
// 缓存taker获取者函数cb,并向cb添加'@@redux-saga/MATCH'属性为次参matcher
take: function take(cb, matcher) {
if (arguments.length > 1) {
(0, _utils.check)(matcher, _utils.is.func, 'channel.take\'s matcher argument must be a function');
cb[_utils.MATCH] = matcher;// _utils.MATCH即'@@redux-saga/MATCH'
}
chan.take(cb);
}
});
}
buffers.js作为缓存
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.buffers = exports.BUFFER_OVERFLOW = undefined;
var _utils = require('./utils');
// ringBuffer函数,缓存arr超过limit长度限制时的错误提示
var BUFFER_OVERFLOW = exports.BUFFER_OVERFLOW = 'Channel\'s Buffer overflow!';
// ringBuffer函数,缓存arr超过limit长度限制,抛出错误
var ON_OVERFLOW_THROW = 1;
// ringBuffer函数,缓存arr超过limit长度限制,忽略,不作处理
var ON_OVERFLOW_DROP = 2;
// ringBuffer函数,缓存arr超过limit长度限制,替换处理
var ON_OVERFLOW_SLIDE = 3;
// ringBuffer函数,缓存arr超过limit长度限制,将arr长度扩展为2*limit
var ON_OVERFLOW_EXPAND = 4;
// 返回{ isEmpty:()=>{return true;}, put:()=>{}, take:()=>{} }
var zeroBuffer = { isEmpty: _utils.kTrue, put: _utils.noop, take: _utils.noop };
function ringBuffer() {
var limit = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 10;
var overflowAction = arguments[1];
var arr = new Array(limit);
var length = 0;// 追踪arr缓存的长度
var pushIndex = 0;// push方法存储数组项的序号值,超过limit,自动清零
var popIndex = 0;// take方法获取数组项的序号值,超过limit,自动清零
// 向arr缓存的pushIndex位置添加数组项,pushIndex在不超过limit限制下加1,追踪arr缓存的长度length值加1
var push = function push(it) {
arr[pushIndex] = it;
pushIndex = (pushIndex + 1) % limit;
length++;
};
// 向arr缓存的popIndex位置获取数组项,popIndex在不超过limit限制下加1,追踪arr缓存的长度length值减1
var take = function take() {
if (length != 0) {
var it = arr[popIndex];
arr[popIndex] = null;
length--;
popIndex = (popIndex + 1) % limit;
return it;
}
};
// 从popIndex序号位置起始获取arr缓存中的所有数组项,以数组形式返回,arr数组项均置为null
var flush = function flush() {
var items = [];
while (length) {
items.push(take());
}
return items;
};
return {
// 判断arr数组缓存是否尚有数据
isEmpty: function isEmpty() {
return length == 0;
},
// arr长度小于limit,向arr添加数组项;超过,根据情况报错,或替换数组项,或数组长度翻倍
put: function put(it) {
if (length < limit) {
push(it);
} else {
var doubledLimit = void 0;
switch (overflowAction) {
// arr超过limit长度限制时,报错处理
case ON_OVERFLOW_THROW:
throw new Error(BUFFER_OVERFLOW);
// arr超过limit长度限制时,pushIndex位置数组项替换为当前压入数组的it值
case ON_OVERFLOW_SLIDE:
arr[pushIndex] = it;
pushIndex = (pushIndex + 1) % limit;
popIndex = pushIndex;
break;
// arr超过limit长度限制时,将arr长度扩展为2*limit
case ON_OVERFLOW_EXPAND:
doubledLimit = 2 * limit;
arr = flush();
length = arr.length;
pushIndex = arr.length;
popIndex = 0;
arr.length = doubledLimit;
limit = doubledLimit;
push(it);
break;
// 忽略,不作处理
default:
// DROP
}
}
},
take: take,
flush: flush
};
}
var buffers = exports.buffers = {
// 返回{ isEmpty:()=>{return true;}, put:()=>{}, take:()=>{} } 不作缓存
none: function none() {
return zeroBuffer;
},
// 获取limit长度的数组缓存{isEmpty,put,take,flush}
// isEmpty判断数组缓存是否为空;put储值,当数组缓存超过limit长度时,报错;take取值;flush获取数组缓存的备份
fixed: function fixed(limit) {
return ringBuffer(limit, ON_OVERFLOW_THROW);
},
// 获取limit长度的数组缓存{isEmpty,put,take,flush};当数组缓存超过limit长度时执行put方法,忽略,不作处理
dropping: function dropping(limit) {
return ringBuffer(limit, ON_OVERFLOW_DROP);
},
// 获取limit长度的数组缓存{isEmpty,put,take,flush};当数组缓存超过limit长度时执行put方法,作数组项替换处理
sliding: function sliding(limit) {
return ringBuffer(limit, ON_OVERFLOW_SLIDE);
},
// 获取limit长度的数组缓存{isEmpty,put,take,flush};当数组缓存超过limit长度时执行put方法,将数组长度翻倍
expanding: function expanding(initialSize) {
return ringBuffer(initialSize, ON_OVERFLOW_EXPAND);
}
};
scheduler.js管控task执行流程
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.asap = asap;
exports.suspend = suspend;
exports.flush = flush;
var queue = [];
// 无task任务函数执行时,semaphore为0;task任务函数执行时,semaphore自增1;task任务函数执行完成,semaphore自减1
var semaphore = 0;
// 执行task任务,并取出queue中的所有任务函数执行,semaphore置为0
function exec(task) {
try {
suspend();
task();
} finally {
flush();
}
}
// 若semaphore为0,执行task任务函数及queue中的所有任务函数;不为0,将task推入queue中
function asap(task) {
if (!semaphore) {
exec(task);
} else {
queue.push(task);
}
}
// semaphore自增1
function suspend() {
semaphore++;
}
// queue中仍有任务函数,取出并执行
function flush() {
semaphore--;
if (!semaphore && queue.length) {
exec(queue.shift());
}
}
sagaHelpers用于创建监听特定action的saga
主要目的是控制proc.js模块mainTask主任务函数的运作流程,用于设定子任务流程的有效期
takeEvery(pattern,saga)
当redux派发的action匹配pattern时,以saga构建子task并执行;多次派发action,将构建多个子task并先后执行
takeLatest(pattern,saga)
当redux派发的action匹配pattern时,以saga构建子task并执行; 多次派发action,将取消前次构建的子task,随后新建子task并执行
takeLatest(delay,pattern,saga)
当redux派发的action匹配pattern时,以saga构建子task并执行; 多次派发action,延迟delay时间的action有效
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.throttle = exports.takeLatest = exports.takeEvery = undefined;
var _slicedToArray = function () {
// 参数arr若为数组,获取长度为i的该数组备份;若为对象且可迭代,获取长度为i的迭代内容,构成数组返回
function sliceIterator(arr, i) {
var _arr = [];
var _n = true;
var _d = false;
var _e = undefined;
try {
for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) {
_arr.push(_s.value);
if (i && _arr.length === i) break;
}
} catch (err) {
_d = true;
_e = err;
} finally {
try {
if (!_n && _i["return"]) _i["return"]();
} finally {
if (_d) throw _e;
}
}
return _arr;
}
return function (arr, i) {
if (Array.isArray(arr)) {
return arr;
} else if (Symbol.iterator in Object(arr)) {
return sliceIterator(arr, i);
} else {
throw new TypeError("Invalid attempt to destructure non-iterable instance");
}
};
}();
exports.takeEveryHelper = takeEveryHelper;
exports.takeLatestHelper = takeLatestHelper;
exports.throttleHelper = throttleHelper;
var _channel = require('./channel');
var _utils = require('./utils');
var _io = require('./io');
var _buffers = require('./buffers');
var done = { done: true, value: undefined };
var qEnd = {};
// fsmIterator(fsm,q0,name)
// 参数fsm={q[i]:()=>{return [q[j],outPut,updateState]}} 设定next方法启用执行分支,
// 分支返回值q[j]设定下一个执行分支,outPut作为当前执行分支的返回值,updateState当next方法再次执行时调用
// 示例:next方法调用时首先执行q0,返回{return:"q1"};再次执行next方法时,调用updateState,迭代终止
// fsmIterator({
// q0: ()=>{
// return ["q1",{return:"q1"},updateState(arg)=>{}]
// },
// q1: ()=>{
// return [qEnd]
// },
// }, q0)
function fsmIterator(fsm, q0) {
var name = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 'iterator';
var updateState = void 0,
qNext = q0;
function next(arg, error) {
if (qNext === qEnd) {
return done;
}
if (error) {
qNext = qEnd;
throw error;
} else {
updateState && updateState(arg);
var _fsm$qNext = fsm[qNext](),
_fsm$qNext2 = _slicedToArray(_fsm$qNext, 3),
q = _fsm$qNext2[0],
output = _fsm$qNext2[1],
_updateState = _fsm$qNext2[2];
qNext = q;
updateState = _updateState;
return qNext === qEnd ? done : output;
}
}
// utils.makeIterator(next,thro,name,isHelper)构建迭代器
// 返回{name,next,throw,return};同时添加[Symbol.iterator]属性使其和原生迭代器相同,也返回{name,next,throw,return}
// utils.makeIterator(next)[Symbol.iterator]()含有next方法进行迭代,throw方法抛出错误
// return方法返回迭代终值,name属性迭代器名
return (0, _utils.makeIterator)(next, function (error) {
return next(null, error);
}, name, true);
}
function safeName(patternOrChannel) {
if (_utils.is.channel(patternOrChannel)) {
return 'channel';
} else if (Array.isArray(patternOrChannel)) {
return String(patternOrChannel.map(function (entry) {
return String(entry);
}));
} else {
return String(patternOrChannel);
}
}
// 构造迭代器,当派发的action满足pattern时,以worker构建子task并执行;多次派发action,将调用worker构建多个子task
// 1 next() q1分支,返回{ done: false, value: { [IO]:true, TAKE:{ pattern | channel } } }
// 2 next(action1) q2分支,返回{ done:false, value:{ [IO]:true, FORK:{context:undefined,fn:worker,args:[...args,action1]} } }
// { done:false, value:{ [IO]:true, FORK:{context:undefined,fn:worker,args:[...args,action]} } }
// 当action为{ type: '@@redux-saga/CHANNEL_END' }时,返回{ done: true, value: undefined }
// 以fsmIterator函数构造迭代器,takeEveryHelper调用后返回含有next方法的迭代器
// fsmIterator首参设定迭代器next方法的各策略分支,次参决定next方法首次调用时执行哪个策略分支
// 各策略分支的返回值首项约定下一个执行的策略分支,第二项作为当前分支的返回值,第三项设定fsmIterator的缓存updateState
// 首次调用next方法,执行q1分支,将fsmIterator的缓存updateState设为setAction,同时返回yTake,约定二次调用next方法时执行q2分支
// 再次调用next方法,执行q2分支,根据next参数设置action,迭代终结或者返回yFork={context,fn:worker,args:action}
function takeEveryHelper(patternOrChannel, worker) {
for (var _len = arguments.length, args = Array(_len > 2 ? _len - 2 : 0), _key = 2; _key < _len; _key++) {
args[_key - 2] = arguments[_key];
}
// io.take(patternOrChannel) 返回{ [IO]:true, TAKE:{pattern} }或{ [IO]:true, TAKE:{channel} }
// yTake = { done: false, value: { [IO]:true, TAKE:{ pattern | channel } } };
var yTake = { done: false, value: (0, _io.take)(patternOrChannel) };
// io.fork([context,fn] | {context,fn} | fn,...args) 返回{ [IO]:true, FORK:{context,fn,args} }
// yFork函数返回 { done:false, value:{ [IO]:true, FORK:{context:undefined,fn:worker,args:[...args,ac]} } }
var yFork = function yFork(ac) {
return { done: false, value: _io.fork.apply(undefined, [worker].concat(args, [ac])) };
};
var action = void 0,
setAction = function setAction(ac) {
return action = ac;
};
// 返回迭代器首次调用next方法,执行q1分支,约定q2为下次next方法执行分支,同时下次next方法执行时调用setAction处理参数
// yTake作为本次next方法调用时的返回值
// 再次调用next方法,执行q2分支,执行setAction函数设置缓存action,action为{ type: '@@redux-saga/CHANNEL_END' }时,迭代终止
// 否则下次next方法重新执行q1分支,yFork(action)作为本次next方法调用时的返回值
return fsmIterator({
q1: function q1() {
return ['q2', yTake, setAction];
},
q2: function q2() {
return action === _channel.END ? [qEnd] : ['q1', yFork(action)];
}
}, 'q1', 'takeEvery(' + safeName(patternOrChannel) + ', ' + worker.name + ')');
}
// 构造迭代器,当派发的action满足pattern时,以worker构建子task并执行;多次派发action,将清除前次子task,构建新的子task并执行
// 1 next() q1分支,返回{ done: false, value: { [IO]:true, TAKE:{ pattern | channel } } }
// 2 next(action1) q2分支,返回{ done:false, value:{ [IO]:true, FORK:{context:undefined,fn:worker,args:[...args,action1]} } }
// 以worker函数构建子task并返回,作为saga运行过程中next函数的参数
// 3 next(task) q1分支,返回{ done: false, value: { [IO]:true, TAKE:{ pattern | channel } } }
// 4 next(action2) q2分支,返回{ done: false, value: { [IO]:true, CANCEL:task } }
// 以redux机制再次派发action,清除前次task任务
// 5 next() q3分支,返回{ done:false, value:{ [IO]:true, FORK:{context:undefined,fn:worker,args:[...args,action2]} } }
// 6 next(task) q1分支,返回{ done: false, value: { [IO]:true, TAKE:{ pattern | channel } } }
// 重复步骤4-6,知道task为否值,重复步骤2
// 或action为{ type: '@@redux-saga/CHANNEL_END' }时,迭代终止,返回{ done: true, value: undefined }
// 以fsmIterator函数构造迭代器,takeEveryHelper调用后返回含有next方法的迭代器
// fsmIterator首参各分支策略,次参决定next方法首次调用时执行哪个分支策略
// 各策略分支的返回值首项约定下一个执行的策略分支,第二项作为当前分支的返回值,第三项设定fsmIterator的缓存updateState
// 首次调用next方法,执行q1分支,将fsmIterator的缓存updateState设为setAction
// 同时返回yTake,约定二次调用next方法时执行q2分支
// 再次调用next方法,执行q2分支,根据next参数设置action,将updateState设为setTask
// 返回yFork={context,fn:worker,args:action},约定三调用next方法时执行q1分支
// 三次调用next方法,执行q1分支,根据next参数设置task,将updateState设为setAction
// 同时返回yTake,约定二次调用next方法时执行q2分支
// 四次调用next方法,执行q2分支,根据next参数设置action,返回yTake,根据前次设定的task值进入q3或q1分支
function takeLatestHelper(patternOrChannel, worker) {
for (var _len2 = arguments.length, args = Array(_len2 > 2 ? _len2 - 2 : 0), _key2 = 2; _key2 < _len2; _key2++) {
args[_key2 - 2] = arguments[_key2];
}
// io.take(patternOrChannel) 返回{ [IO]:true, TAKE:{pattern} }或{ [IO]:true, TAKE:{channel} }
// yTake = { done: false, value: { [IO]:true, TAKE:{ pattern | channel } } };
var yTake = { done: false, value: (0, _io.take)(patternOrChannel) };
// io.fork([context,fn] | {context,fn} | fn,...args) 返回{ [IO]:true, FORK:{context,fn,args} }
// yFork函数返回 { done:false, value:{ [IO]:true, FORK:{context:undefined,fn:worker,args:[...args,ac]} } }
var yFork = function yFork(ac) {
return { done: false, value: _io.fork.apply(undefined, [worker].concat(args, [ac])) };
};
// io.cancel(...tasks) 若tasks为多项,返回[{ [IO]:true, CANCEL:task }];只有一项,返回{ [IO]:true, CANCEL:task }
// yCancel函数返回 { done: false, value: { [IO]:true, CANCEL:task } }
var yCancel = function yCancel(task) {
return { done: false, value: (0, _io.cancel)(task) };
};
var task = void 0,
action = void 0;
var setTask = function setTask(t) {
return task = t;
};
var setAction = function setAction(ac) {
return action = ac;
};
// 返回迭代器首次调用next方法,执行q1分支,约定q2为下次next方法执行分支,同时下次next方法执行时调用setAction处理参数
// yTake作为本次next方法调用时的返回值
// 再次调用next方法,执行q2分支,执行setAction函数设置缓存action,action为{ type: '@@redux-saga/CHANNEL_END' }时,迭代终止
// task缓存有值,下次next方法执行时进入q3分支,yCancel(task)作为本次next方法调用时的返回值
// 否则下次next方法重新执行q1分支,同时下次next方法执行时调用setTask处理参数,yFork(action)作为本次next方法调用时的返回值
return fsmIterator({
q1: function q1() {
return ['q2', yTake, setAction];
},
q2: function q2() {
return action === _channel.END ? [qEnd] : task ? ['q3', yCancel(task)] : ['q1', yFork(action), setTask];
},
q3: function q3() {
return ['q1', yFork(action), setTask];
}
}, 'q1', 'takeLatest(' + safeName(patternOrChannel) + ', ' + worker.name + ')');
}
// 构造迭代器,当redux派发的action匹配pattern时,以saga构建子task并执行;多次派发action,延迟delay时间的action有效
// 1 next() q1分支,返回{ done: false, value: { [IO]:true, ACTION_CHANNEL:{pattern,buffer} } }
// 2 next(channel) q2分支,返回{ done: false, value: { [IO]:true, TAKE:{ pattern | channel } } }
// 3 next(action) q3分支,返回{ done:false, value:{ [IO]:true, FORK:{context:undefined,fn:worker,args:[...args,action]} } }
// 4 next() q4分支,返回{ done: false, value: { [IO]:true, CALL:{context:undefined,fn:util.delay,args:delayLength} } }
// 5 next() q2分支,返回{ done: false, value: { [IO]:true, TAKE:{ pattern | channel } } }
// 6 next(action) q3分支,返回{ done:false, value:{ [IO]:true, FORK:{context:undefined,fn:worker,args:[...args,action]} } }
// 重复步骤4-6
// 当q3分支action为{ type: '@@redux-saga/CHANNEL_END' }时,迭代终止,返回{ done: true, value: undefined }
function throttleHelper(delayLength, pattern, worker) {
for (var _len3 = arguments.length, args = Array(_len3 > 3 ? _len3 - 3 : 0), _key3 = 3; _key3 < _len3; _key3++) {
args[_key3 - 3] = arguments[_key3];
}
var action = void 0,
channel = void 0;
// io.actionChannel(pattern,[buffer]) 返回{ [IO]:true, ACTION_CHANNEL:{pattern,buffer} }
// yActionChannel = { done: false, value: { [IO]:true, ACTION_CHANNEL:{pattern,buffer} } }
var yActionChannel = { done: false, value: (0, _io.actionChannel)(pattern, _buffers.buffers.sliding(1)) };
// io.take(patternOrChannel) 返回{ [IO]:true, TAKE:{pattern} }或{ [IO]:true, TAKE:{channel} }
// yTake函数返回 { done: false, value: { [IO]:true, TAKE:{ pattern | channel } } };
var yTake = function yTake() {
return { done: false, value: (0, _io.take)(channel) };
};
// io.fork([context,fn] | {context,fn} | fn,...args) 返回{ [IO]:true, FORK:{context,fn,args} }
// yFork函数返回 { done:false, value:{ [IO]:true, FORK:{context:undefined,fn:worker,args:[...args,ac]} } }
var yFork = function yFork(ac) {
return { done: false, value: _io.fork.apply(undefined, [worker].concat(args, [ac])) };
};
// io.call([context,fn] | {context,fn} | fn,...args) 返回{ [IO]:true, CALL:{context,fn,args} }
// yDelay = { done: false, value: { [IO]:true, CALL:{context:undefined,fn:util.delay,args:delayLength} } }
var yDelay = { done: false, value: (0, _io.call)(_utils.delay, delayLength) };
var setAction = function setAction(ac) {
return action = ac;
};
var setChannel = function setChannel(ch) {
return channel = ch;
};
return fsmIterator({
q1: function q1() {
return ['q2', yActionChannel, setChannel];
},
q2: function q2() {
return ['q3', yTake(), setAction];
},
q3: function q3() {
return action === _channel.END ? [qEnd] : ['q4', yFork(action)];
},
q4: function q4() {
return ['q2', yDelay];
}
}, 'q1', 'throttle(' + safeName(pattern) + ', ' + worker.name + ')');
}
var deprecationWarning = function deprecationWarning(helperName) {
return 'import ' + helperName + ' from \'redux-saga\' has been deprecated in favor of import '
+ helperName + ' from \'redux-saga/effects\'.\nThe latter will not work with yield*, '
+ 'as helper effects are wrapped automatically for you in fork effect.\nTherefore yield '
+ helperName + ' will return task descriptor to your saga and execute next lines of code.';
};
var takeEvery = exports.takeEvery = (0, _utils.deprecate)(takeEveryHelper, deprecationWarning('takeEvery'));
var takeLatest = exports.takeLatest = (0, _utils.deprecate)(takeLatestHelper, deprecationWarning('takeLatest'));
var throttle = exports.throttle = (0, _utils.deprecate)(throttleHelper, deprecationWarning('throttle'));
对外接口
effects.js
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
var _io = require('./internal/io');
Object.defineProperty(exports, 'take', {
enumerable: true,
get: function get() {
return _io.take;
}
});
Object.defineProperty(exports, 'takem', {
enumerable: true,
get: function get() {
return _io.takem;
}
});
Object.defineProperty(exports, 'put', {
enumerable: true,
get: function get() {
return _io.put;
}
});
Object.defineProperty(exports, 'race', {
enumerable: true,
get: function get() {
return _io.race;
}
});
Object.defineProperty(exports, 'call', {
enumerable: true,
get: function get() {
return _io.call;
}
});
Object.defineProperty(exports, 'apply', {
enumerable: true,
get: function get() {
return _io.apply;
}
});
Object.defineProperty(exports, 'cps', {
enumerable: true,
get: function get() {
return _io.cps;
}
});
Object.defineProperty(exports, 'fork', {
enumerable: true,
get: function get() {
return _io.fork;
}
});
Object.defineProperty(exports, 'spawn', {
enumerable: true,
get: function get() {
return _io.spawn;
}
});
Object.defineProperty(exports, 'join', {
enumerable: true,
get: function get() {
return _io.join;
}
});
Object.defineProperty(exports, 'cancel', {
enumerable: true,
get: function get() {
return _io.cancel;
}
});
Object.defineProperty(exports, 'select', {
enumerable: true,
get: function get() {
return _io.select;
}
});
Object.defineProperty(exports, 'actionChannel', {
enumerable: true,
get: function get() {
return _io.actionChannel;
}
});
Object.defineProperty(exports, 'cancelled', {
enumerable: true,
get: function get() {
return _io.cancelled;
}
});
Object.defineProperty(exports, 'flush', {
enumerable: true,
get: function get() {
return _io.flush;
}
});
Object.defineProperty(exports, 'takeEvery', {
enumerable: true,
get: function get() {
return _io.takeEvery;
}
});
Object.defineProperty(exports, 'takeLatest', {
enumerable: true,
get: function get() {
return _io.takeLatest;
}
});
Object.defineProperty(exports, 'throttle', {
enumerable: true,
get: function get() {
return _io.throttle;
}
});
utils.js
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
var _utils = require('./internal/utils');
Object.defineProperty(exports, 'TASK', {
enumerable: true,
get: function get() {
return _utils.TASK;
}
});
Object.defineProperty(exports, 'SAGA_ACTION', {
enumerable: true,
get: function get() {
return _utils.SAGA_ACTION;
}
});
Object.defineProperty(exports, 'noop', {
enumerable: true,
get: function get() {
return _utils.noop;
}
});
Object.defineProperty(exports, 'is', {
enumerable: true,
get: function get() {
return _utils.is;
}
});
Object.defineProperty(exports, 'deferred', {
enumerable: true,
get: function get() {
return _utils.deferred;
}
});
Object.defineProperty(exports, 'arrayOfDeffered', {
enumerable: true,
get: function get() {
return _utils.arrayOfDeffered;
}
});
Object.defineProperty(exports, 'createMockTask', {
enumerable: true,
get: function get() {
return _utils.createMockTask;
}
});
var _io = require('./internal/io');
Object.defineProperty(exports, 'asEffect', {
enumerable: true,
get: function get() {
return _io.asEffect;
}
});
var _proc = require('./internal/proc');
Object.defineProperty(exports, 'CHANNEL_END', {
enumerable: true,
get: function get() {
return _proc.CHANNEL_END;
}
});
index.js
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.utils = exports.effects = exports.CANCEL = exports.delay = exports.throttle = exports.takeLatest = exports.takeEvery = exports.buffers = exports.channel = exports.eventChannel = exports.END = exports.runSaga = undefined;
var _runSaga = require('./internal/runSaga');
Object.defineProperty(exports, 'runSaga', {
enumerable: true,
get: function get() {
return _runSaga.runSaga;
}
});
var _channel = require('./internal/channel');
Object.defineProperty(exports, 'END', {
enumerable: true,
get: function get() {
return _channel.END;
}
});
Object.defineProperty(exports, 'eventChannel', {
enumerable: true,
get: function get() {
return _channel.eventChannel;
}
});
Object.defineProperty(exports, 'channel', {
enumerable: true,
get: function get() {
return _channel.channel;
}
});
var _buffers = require('./internal/buffers');
Object.defineProperty(exports, 'buffers', {
enumerable: true,
get: function get() {
return _buffers.buffers;
}
});
var _sagaHelpers = require('./internal/sagaHelpers');
Object.defineProperty(exports, 'takeEvery', {
enumerable: true,
get: function get() {
return _sagaHelpers.takeEvery;
}
});
Object.defineProperty(exports, 'takeLatest', {
enumerable: true,
get: function get() {
return _sagaHelpers.takeLatest;
}
});
Object.defineProperty(exports, 'throttle', {
enumerable: true,
get: function get() {
return _sagaHelpers.throttle;
}
});
var _utils = require('./internal/utils');
Object.defineProperty(exports, 'delay', {
enumerable: true,
get: function get() {
return _utils.delay;
}
});
Object.defineProperty(exports, 'CANCEL', {
enumerable: true,
get: function get() {
return _utils.CANCEL;
}
});
var _middleware = require('./internal/middleware');
var _middleware2 = _interopRequireDefault(_middleware);
var _effects = require('./effects');
var effects = _interopRequireWildcard(_effects);
var _utils2 = require('./utils');
var utils = _interopRequireWildcard(_utils2);
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;
}
}
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
exports.default = _middleware2.default;
exports.effects = effects;
exports.utils = utils;
插件引用
- 由package.json中{"name": "redux-saga","main": "lib/index.js"} 提供require("redux-saga")功能
- 由effects/package.json中{"name": "redux-saga/effects","main": "../lib/effects.js"} 提供require("redux-saga/effects")功能
- 由effects/utils.json中{"name": "redux-saga/utils","main": "../lib/utils"} 提供require("redux-saga/utils")功能
相关推荐
Redux-Saga是Redux生态系统中的一个中间件,专为解决React应用中的异步操作而设计。在Redux的传统流程中,Action被创建并发送到Reducer,Reducer根据Action的type更新State,然后Store将新的State分发回组件。然而,...
在“react-redux-saga-architecture-源码”中,我们可以看到以下关键部分: 1. `actions.js`:定义了应用中可能触发的各种action,例如`FETCH_DATA`,`DATA_FETCH_SUCCESS`等。 2. `reducers.js`:包含reducer函数...
在"前端项目-redux-saga.zip"中,包含的"redux-saga-master"文件夹很可能是Redux Saga的源码仓库或者一个示例项目的根目录。这个项目可能包含了以下内容: 1. `src`目录:通常存放源代码,包括Redux Saga的配置、...
Redux-saga中间件使用方法和示例代码
import createSagaMiddleware from 'redux-saga'; import rootReducer from './reducers'; import { composeWithDevTools } from 'redux-devtools-extension'; const middleware = [thunk, createSagaMiddleware, ...
虽然示例中使用的是 `redux-saga-testing`,但标签中还提到了 `redux-saga-test-plan`,这是一个更全面的测试库,提供了更多的工具来测试Saga。例如,它可以让你更好地模拟中间件的行为,或者使用`expectSaga`和`...
用法安装插件:npm npm i -D eslint-plugin-redux-saga 纱yarn add -D eslint-plugin-redux-saga 并将其添加到您的.eslintrc文件中: { " plugins " : [ " redux-saga " ]}规则规则描述推荐的可固定确保产生效果防止...
由于next.js不断扩大,并且其他具有更好支持的软件包涵盖了redux-saga SSR功能。 有关更多信息,请参见 。 针对redux-saga HOC。 用于服务器端渲染的受控redux-saga执行。 注意:从4.0.0版开始,不再支持同步HOC...
React本机Redux入门代码使用Redux,...一个很酷的RN加载器react-native-router-flux-带有redux支持的易于导航/路由器react-native-vector-icons-很棒的图标redux-saga-使用生成器的副作用redux-storage-侧面redux状态树
npm i redux-saga-callback 用法 import { putWait , withCallback } from 'redux-saga-callback' ; 用withCallback包装您的传奇发生器 takeEvery ( 'FETCH_USERS' , withCallback ( fetchUsers ) ) ; 如果需要,...
键入Redux Saga 尝试将更好的TypeScript类型带入redux-saga。 需要TypeScript 3.6或更高版本。 安装 # yarn yarn add typed-redux-saga # npm npm install typed-redux-saga 用法 让我们以为例 前 import { call ,...
在实际项目中,通过与 `redux-thunk`、`redux-saga` 等中间件配合使用,可以实现更复杂的业务逻辑监控,进一步提升开发效率。无论你是新手还是经验丰富的开发者,Redux DevTools 都是构建高质量 Redux 应用不可或缺...
安装npm install use-saga-reducer用法import useSagaReducer from 'use-saga-reducer'import { takeLatest , call , put } from 'redux-saga/effects'function * dataFetcher ( ) { try { const data = yield call ...
redux-saga登录React示例redux-saga登录示例,具有超基本的UI,单元测试和E2E测试。 Redux Saga库在文档上报告了一个有趣的示例,一个完整的登录过程。 你可以在找到它。 因为这是我与生成器的第一次接触,所以我为...
Redux-Saga 文档版本号:1.0.0-beta 英文原版: : redux-saga是一个有用的管理应用程序副作用(异步,例如异步获取数据,访问浏览器缓存等)的库,它的目标是让异步管理更容易,执行更高效,测试更简单,在处理...
redux-saga-examples, redux传奇的另一个示例库 redux-saga-examplesredux传奇的另一个示例库。演示自动完成功能延迟请求调度与取消节气门具有节流并发性的执行队列开始停止操作带页面可见性API的链取消微博插件...