主题
redux-saga 模拟实现
1. channel 的概念
take 是注册一个函数, put 是执行注册的函数。
javascript
function channel() {
let taker;
function take(cb) {
taker = cb;
}
function put(input) {
if (taker) {
const tempTaker = taker;
taker = null;
tempTaker(input);
}
}
return {
put,
take
};
}
const chan = channel();
chan.take((v) => {
console.log(v); // 输出 'hello'
});
chan.put("hello");
2. 一个简单的 demo
javascript
function channel() {
let taker;
function take(cb) {
taker = cb;
}
function put(input) {
if (taker) {
const tempTaker = taker;
taker = null;
tempTaker(input);
}
}
return {
put,
take
};
}
const chan = channel();
function take() {
return {
type: "take"
};
}
function* mainSaga() {
const action = yield take();
console.log(action);
}
function runTakeEffect(effect, cb) {
chan.take((input) => {
cb(input);
});
}
function task(iterator) {
const iter = iterator();
function next(args) {
const result = iter.next(args);
if (!result.done) {
const effect = result.value;
if (effect.type === "take") {
runTakeEffect(result.value, next);
}
}
}
next();
}
task(mainSaga);
let i = 0;
$btn.addEventListener(
"click",
() => {
const action = `action data${i++}`;
chan.put(action);
},
false
);
由于 mainSaga 里的 yield 出来的 take 的 type 为 take, 所以会走到 runTakeEffect 里, 这样就相当于执行了:
javascript
chan.take((input) => {
next(input);
});
next 的迭代器为 mainSaga 里的代码:
javascript
function* mainSaga() {
const action = yield take();
console.log(action);
}
3. 如何监听每一次的事件点击呢?
其实最简单的方式就是改变 mainSaga 为不断的循环, 代码如下:
javascript
function* mainSaga() {
while (true) {
const action = yield take();
console.log(action);
}
}
4. saga 里面提供了 takeEvery, 其实最终和上面的效果是一样的
javascript
const $btn = document.querySelector("#test");
const $result = document.querySelector("#result");
function channel() {
let taker;
function take(cb) {
taker = cb;
}
function put(input) {
if (taker) {
const tempTaker = taker;
taker = null;
tempTaker(input);
}
}
return {
put,
take
};
}
const chan = channel();
let i = 0;
$btn.addEventListener(
"click",
() => {
chan.put(`action data${i++}`);
},
false
);
function take() {
return {
type: "take"
};
}
function fork(cb) {
return {
type: "fork",
fn: cb
};
}
function* takeEvery(worker) {
yield fork(function* () {
while (true) {
const action = yield take();
worker(action); // 5
}
});
}
function* mainSaga() {
yield takeEvery((action) => {
$result.innerHTML = action;
});
}
function runTakeEffect(effect, cb) {
// 5
chan.take((input) => {
cb(input);
});
}
function runForkEffect(effect, cb) {
task(effect.fn || effect); // 2
cb();
}
function task(iterator) {
const iter = typeof iterator === "function" ? iterator() : iterator;
function next(args) {
const result = iter.next(args); // 3
if (!result.done) {
const data = result.value;
if (typeof data[Symbol.iterator] === "function") {
runForkEffect(data, next); // 1
} else if (data.type) {
switch (data.type) {
case "take":
runTakeEffect(result.value, next);
break;
case "fork":
runForkEffect(result.value, next); // 4
break;
default:
break;
}
}
}
}
next();
}
task(mainSaga);
/*
1. task(mainSaga) 执行
2. runForkEffect(data, next); -- 1
3. 相当于执行 task(takeEvery); -- 2
4. result 的值为: -- 3
{
done: false,
value: {
type: 'fork',
fn: function* () {
while(true) {
const action = yield take();
worker(action);
}
},
}
}
5. 判断value.type 为 'fork' 走到 runForkEffect --- 4
6. 相当于执行 take(function* () {
while(true) {
const action = yield take();
worker(action);
}
);
7. take函数里执行时, result 的值为 -- 3
{
done: false,
value: {
type: 'take'
}
}
8. 由于type 为 'take', 执行 runTakeEffect, 相当于执行了 -- 5
chan.take(input => {
next(input);
});
8. 当点击按钮的时候执行 chan.put(`action data${i++}`),
会执行
chan.take(input => {
next(`action data${i++}`);
});
9. next(`action data${i++}`) 的执行, 因为这个时候 iterator 为
function* () {
while(true) {
const action = yield take();
worker(action);
}
}
所有相当于执行了 worker(action);
work 为 action => {
$result.innerHTML = action;
}
9. 由于是 while(true), 所有上面的 next 执行又到了 yield take();
又执行了 runTakeEffect({type: 'take'}, next);
*/