目录
一、基础知识
1.1 生成器函数Generator
// 前面加*才会成为生成器函数
function* test() {
console.log(1111);
var input1 = yield "1111暂停"; // 调用gen.next()会将yield的结果 传给res1
console.log(2222, input1); //通过gen.next(bbbb)进行传参,可以得到结果,为什么不是aaaa,因为第一次console.log(1111);执行后就停止
yield "2222暂停";
console.log(3333);
yield "3333暂停";
}
let gen = test();
let res1 = gen.next("aaaa"); //调用next方法,遇到yield就停止
let res2 = gen.next("bbbb");
let res3 = gen.next("cccc");
let res4 = gen.next(); // 后面没东西了
console.log(res1); //返回的是个对象{value:...,done:...}
console.log(res2);
console.log(res3);
console.log(res4); // done:true,表示生成器执行完毕
1.2 可执行生成器
function getData1() {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(["data1"]);
}, 1000);
});
}
function getData2(data) {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve([...data, "data2"]);
}, 1000);
});
}
function getData3(data) {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve([...data, "data3"]);
}, 1000);
});
}
function* gen() {
let f1 = yield getData1();
console.log(f1);
let f2 = yield getData2(f1);
console.log(f2);
let f3 = yield getData3(f2);
console.log(f3);
}
function run(fn) {
let g = fn();
function next(data) {
let result = g.next(data);
if (result.done) {
return result.value;
}
result.value.then((res) => {
next(res);
});
}
next();
}
run(gen);
二、redux-saga
2.1 核心概念
Redux-Saga 是一个用于管理 Redux 应用异步操作(副作用)的中间件库。它使用 ES6 的 Generator 函数让异步流程更易读、更易测试。
在saga中,全局监听器和接收器使用Generator函数和saga自身的一些辅助函数实现对整个流程的管控
2.1.1 Saga
Saga 是一个 Generator 函数,它使用 yield
关键字来暂停和恢复执行。Saga 负责组织和控制应用的副作用(如数据获取、缓存访问等)。
2.1.2 Effect
Effect 是一个纯 JavaScript 对象,包含了一些将被 saga 中间件执行的指令。常见的 Effect 创建器包括:
take
: 等待指定的 actionput
: 发起一个 actioncall
: 调用一个函数(通常是异步的)fork
: 非阻塞地执行一个任务all
: 并行运行多个 Effect
2.1.3 工作原理
Redux-Saga 作为中间件运行在 Redux 应用中:
监听特定的 Redux action
执行相应的副作用逻辑
可能派发新的 action 来更新 store
2.2 基本使用
2.2.1 安装
npm redux redux-saga
2.2.2 目录结构(以多saga、多reducer为例)
在目录结构中,reducer与传统redux配置一致,store/index.js在传统配置中添加了saga,store/saga文件夹下的saga1是基础示例,saga2是应用了链式调用(与 async await相似)
2.2.3 配置
2.2.3.1 配置reducer
目录:store/reducer文件夹
step1:创建reducer
// store/reducer/reducer1
const initState = {
list1: [],
};
function reducer(prevState = initState, action) {
let newState = { ...prevState };
switch (action.type) {
case "change-list1":
newState.list1 = action.payload;
return newState;
default:
return newState;
}
}
export default reducer;
// store/reducer/reducer2
const initState = {
list2: [],
};
function reducer(prevState = initState, action) {
let newState = { ...prevState };
switch (action.type) {
case "change-list2":
newState.list2 = action.payload;
return newState;
default:
return newState;
}
}
export default reducer;
step2:结合多个reducer
// store/reducer/index.js
import { combineReducers } from "redux";
import reducer1 from "./reducer1";
import reducer2 from "./reducer2";
export default combineReducers({
r1: reducer1, // 前面的名字随便取,只要store.getState().r1对应
r2: reducer2,
});
2.2.3.2 配置saga
目录:store/saga文件夹
step1:创建saga
// store/saga/saga1.js
import { call, put } from "redux-saga/effects";
function* getList1() {
// 异步处理
// call函数发送异步请求 call的传参是个Promise对象
let res = yield call(getListAction1);
// put函数发出新的action
yield put({ type: "change-list1", payload: res });
}
function getListAction1() {
return new Promise((resolve) => {
setTimeout(() => {
resolve(["111", "222", "333"]);
}, 100);
});
}
export { getList1 };
// store/saga/saga2.js
import { call, put } from "redux-saga/effects";
function* getList2() {
// 异步处理
// call函数发送异步请求 call的传参是个Promise对象
let res1 = yield call(getListAction2_1); // 阻塞调用
let res2 = yield call(getListAction2_2, res1); // 第二个参数会将结果传参给getListAction2_2
// put函数发出新的action
yield put({ type: "change-list2", payload: res2 });
}
function getListAction2_1() {
return new Promise((resolve) => {
setTimeout(() => {
resolve(["444", "555", "666"]);
}, 1000);
});
}
function getListAction2_2(data) {
return new Promise((resolve) => {
setTimeout(() => {
resolve([...data, "777", "888", "999"]);
}, 1000);
});
}
export { getList2 };
step2:监听多个saga
// store/saga/index.js
import { takeEvery } from "redux-saga/effects";
import { getList1 } from "./saga1";
import { getList2 } from "./saga2";
function* watchSaga() {
console.log(111);
yield takeEvery("get-list1", getList1);
yield takeEvery("get-list2", getList2);
}
export default watchSaga;
2.2.3.3 配置store
// store/index.js
import { createStore, applyMiddleware } from "redux";
import reducer from "./reducer";
import createSagaMidlleWare from "redux-saga";
import watchSaga from "./saga/index";
const sagaMiddleWare = createSagaMidlleWare();
const store = createStore(reducer, applyMiddleware(sagaMiddleWare));
sagaMiddleWare.run(watchSaga); // 运行saga任务
export default store;
2.2.4 示例
2.2.4.1 测试代码
import React from "react";
import store from "./store";
// import "./02-可执行生成器";
export default function App() {
const fetchClick1 = () => {
if (store.getState().r1.list1.length === 0) {
//dispatch
store.dispatch({ type: "get-list1" });
} else {
console.log("缓存1111", store.getState().r1.list1);
}
};
const fetchClick2 = () => {
if (store.getState().r2.list2.length === 0) {
//dispatch
store.dispatch({ type: "get-list2" });
} else {
console.log("缓存2222", store.getState().r2.list2);
}
};
return (
<div>
<button onClick={fetchClick1}>click-ajax-异步缓存1111</button>
<button onClick={fetchClick2}>click-ajax-异步缓存2222</button>
</div>
);
}
2.2.4.2 运行结果
对按钮进行点击,第1次点击为异步请求,第2次点击为缓存