web前端开发|redux-saga简易实现
index.js:
import { channel } from './channel'//promise判断
const isPromise = p => {
return typeof p.then == 'function' && typeof p.catch == 'function'
}function createSagaMiddelware() {
function sagaMiddelware({getState,dispatch}){
//负责把gernerator执行完毕
function run(iterator){
//执行得到迭代器,next得到值,可能是器也可能是迭代器
let it = typeof iterator == 'function'?iterator():iterator;
function next(input){
let {value: effect,done}=it.next(input);
//如果迭代器没有完成
if(!done){
//genrator
if(typeof effect[Symbol.iterator] == 'function'){
run(effect);
next();
}
//延迟函数
if(isPromise(effect)) {
effect
.then(next)
.catch(error => next(error))
}
switch(effect.type){
//注册事件
case 'take':
let {actionType}=effect;
channel.subscribe(actionType,next);
break;
//走到put的事件就直接dispatch执行了
case 'put':
let {action}=effect;
dispatch(action);
next(action);
break;
//fork继续执行
case 'fork':
let {worker}=effect;
run(worker);
next();
break;
//异步执行成功后再next
case 'call':
let {fn,args}=effect;
fn(...args).then(next);
break;
default:
break;
}
}
}
next()
}
sagaMiddelware.run = run;
//中间件执行
return (next) => (action) => {
next(action)
channel.publish(action)
}
}
return sagaMiddelware;
}
export default createSagaMiddelware;
channel.js
function createChannel() {
//对象每一个动作对应一个回调函数
let takers={};
function subscribe(actionType,cb) {
takers[actionType]=cb;
}
function publish(action) {
//监听
let taker=takers[action.type]
if (taker) {//如果有执行监听函数并且删除监听函数
let tmp=taker;
taker = null;
tmp(action);
}
}
return {subscribe,publish};
}
export let channel = createChannel();
effect.js
function take(actionType) {
return {
type: 'take',
actionType
}
}
function put(action) {
return {
type: 'put',
action
}
}
function fork(worker) {
return {
type: 'fork',
worker
}
}
function call(fn,...args) {
return {
type: 'call',
fn,
args
}
}
//监听每一个动作类型,当此动作发生的时候执行对应的worker
//takeEvery它会单开一个任务,并不会阻塞当前saga
function* takeEvery(actionType,worker) {
yield fork(function* () {
while (true) {
let action = yield take(actionType);
yield worker(action);
}
})
}function delay(ms, val = true) {
let timeoutId
const promise = new Promise(resolve => {
timeoutId = setTimeout(() => resolve(val), ms)
})promise['CANCEL_PROMISE'] = () => clearTimeout(timeoutId)return promise
}//takerEvery的结果是一个迭代器
//iterator
export {
take,
put,
takeEvery,
call,
delay
}
使用:
app.js:
import React from 'react';
import ReactDOM from 'react-dom';
import Root from './router';
import {createStore, applyMiddleware } from 'redux';
import { Provider } from 'react-redux';
import createSagaMiddleware from './testSaga/index.js'
// import createSagaMiddleware from './saga/index'
import { watchIncrementAsync, watchAndLog } from './sagaActions/index'//globe css
import './style/index.styl';
import './style/less.less';
import './style/sass.sass';
import './style/scss.scss';
const initialState = {
number: 0,
list: []
};
const incrementReducer = (state = initialState, action) => {
switch(action.type) {
case 'INCREMENT': {
state.number += 1
return { ...state }
break
}
case 'DECREMENT': {
return {
...state,
list: action.data.data
}
break
};
default: return state;
}
};
const sagaMiddleware = createSagaMiddleware();
const store = createStore(incrementReducer,applyMiddleware(sagaMiddleware))
sagaMiddleware.run(watchIncrementAsync)
// sagaMiddleware.run(watchAndLog)ReactDOM.render(
,
document.getElementById('app')
);
sagaAction:
// import { delay } from '../saga'
// import { select, call, fork, take, put, takeEvery } from '../saga/effects'
import { call, put, takeEvery, take, delay } from '../testSaga/effect'
import {GetUserData} from '../fetch/api.js'export function* watchAndLog() {
while (true) {
const action = yield take('*')
const state = yield select()console.log('action', action)
console.log('state after', state)
}
}export function* incrementAsync() {
yield delay(1000)
yield put({ type: 'INCREMENT' })
}export function* indecrementAsyncs({ payload }) {
//发起请求payload是给请求函数的参数
const data = https://www.it610.com/article/yield call(GetUserData, payload);
yield put({ type:'DECREMENT', data })
}//发起请求
function* fetchUrl(param) {
const data = https://www.it610.com/article/yield call(GetUserData, param);
// 指示中间件调用 fetch 异步任务
yield put({ type:'DECREMENT', data });
// 指示中间件发起一个 action 到 Store
}export function* watchIncrementAsync() {
yield takeEvery('INCREMENT_ASYNC', incrementAsync)
yield takeEvery('DECREMENT_ASYNC', indecrementAsyncs)
//或者
// while(true) {
//const action = yield take('FETCH_REQUEST');
// 指示中间件等待 Store 上指定的 action,即监听 action
//yield fork(fetchUrl, action.payload);
// 指示中间件以无阻塞调用方式执行 fetchUrl
// }
}
发起事件:
onClick() {
this.props.dispatch({
type: 'DECREMENT_ASYNC',
// type: 'FETCH_REQUEST',
//参数的传递
payload: {
name: 'test',
s:11
}
})
}
执行流程分析:
初始化阶段:
执行createSagaMiddelware 返回 sagaMiddelware
执行 saga run方法 传入的是自定义的 watchIncrementAsync函数:这里面是takeEvery命令
拿到 iterator (watchIncrementAsync)并执行一下
初次执行一下 next 函数,此时next中 input参数为undefined
执行generator 的 next 获取 watchIncrementAsync中 第一个yield(takeEvery类型)的值
如果里面是iterator则继续run next 最终走到 fork ,fork里面因为是generator函数 则继续执行 run next
走到 type take 往channel监听器中添加 actionType 和 netx函数回调 这样dispatch 就可以执行这个next回调
初始化阶段完毕
【web前端开发|redux-saga简易实现】点击dispatch执行阶段:
dispatch takeEvery中的类型 找到初始化阶段添加注册的此类型并执行 netx 回调
然后it.next(input) 触发的是 yield worker(action); worker就是incrementAsync函数因为他是generator函数 所以会继续执行run()后 再next就可以继续yield
如果其中有 自定义 delay 延迟函数,则等待then结束再回调 next
worker此时是逻辑执行函数如果是
yield put 就直接执行 dispatch(action); 并且next一下,后面可能有多个yield
yield call 执行fn(…args).then(next);
推荐阅读
- 深入理解Go之generate
- 标签、语法规范、内联框架、超链接、CSS的编写位置、CSS语法、开发工具、块和内联、常用选择器、后代元素选择器、伪类、伪元素。
- 私有化轻量级持续集成部署方案--03-部署web服务(下)
- Jsr303做前端数据校验
- web网页模板|如此优秀的JS轮播图,写完老师都沉默了
- 我的软件测试开发工程师书单
- spring|spring boot项目启动websocket
- echart|echart 双轴图开发
- NPDP拆书(三)(新产品开发战略(经营与创新战略))
- 7、前端--jQuery简介、基本选择器、基本筛选器、属性选择器、表单选择器、筛选器方法、节点操作、绑定事件