You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
exportfunctionstdChannel(){constchan=multicastChannel();const{ put }=chan;chan.put=input=>{// saga的action,不进入调度状态if(input[SAGA_ACTION]){put(input);return;}asap(()=>{put(input);});};returnchan;}
我们知道redux-saga 也是通过中间件的形式与 redux 本身连接起来。例如下面使用了redux-saga的react项目需要以下这样的 初始化
所以分析 redux-saga 的第一步,就从 redux-saga 的中间件开始。我们平时写代码在 react 中与 saga 进行交互,都是
dispatch
一个action
到与我们的 saga 逻辑进行交互。翻看createSagaMiddleware
源码,可以很清晰的看到,这就是使用了中间件后,我们每次dispatch
一个 action 后,在 saga 中间件内会往channel
内put
这个action
,进而触发我们 saga 里面的逻辑。就实现了 react 组件和 saga 的交互了。那么这个stdChannel
是什么呢在了解 saga 的运行机制之前,先学习 redux-saga 源码内部的两个比较常用的数据结构
buffer
和chanel
。buffer
buffer 是一个固定长度类似队列的数据结构,它有四种类型(下面介绍),对外暴露了几个函数,如下
我们知道如果我们直接使用数组的 push/unshift(pop/shift)函数实现队列的话,当我们出队列的时候时间复杂度是
o(n)
。而这里的 buffer 实现是比较巧妙的。数据存储是使用定长数组。通过pushIndex
和popIndex
标识位来记录出入队列的位置,它们的初始值都是 0,出队列的时候直接把popIndex
位置空,然后值+1。入队列则是pushIndex
+1。这样,无论take
还是put
,时间复杂度都是o(1)
当
pushIndex
达到了 buffer 的长度的时候,buffer 的处理会根据 buffer 类型不同进行处理1、
ON_OVERFLOW_THROW
:超出限制直接报错2、
ON_OVERFLOW_SLIDE
:类似于环状队列,达到长度限制后,从索引 0 继续存储。3、
ON_OVERFLOW_EXPAND
:达到限制后,长度自动变大 2 倍。4、
ON_OVERFLOW_DROP
:达到限制后,后续的都丢弃chanel
chanel 的实现是类似发布/订阅的设计模式。
chanel.take(taker)
存入一个 taker 函数,chanel.put(action)
时,取出 cb 函数执行,action 是用来消费 taker 的特点:当
put
一个 action 时,如果没有taker
的时候,会将这个 action 存起来,存 action 是用了上面提到的buffer
这个数据结构。等到有 taker 的时候可以马上调用 action。一个简化版的单播 chanel 实现如下
eventChanel 是在普通 Chanel 基础上实现,是用来用于订阅外部的事件源。
chanel
的一些使用参考可以看文档简化的 eventChanel 实现如下,其实给订阅函数传进一个函数,调用这个函数可以往 Chanel 内 put 东西。
从上面的中间件源码可以看到,redux-saga 默认情况下的
Chanel
是stdChannel
。stdChannel
就是基于多播 chanel (multiCastChanel)实现,只不过添加了redux-saga
本身的调度系统。multiCastChanel
和 nodejs 的eventEmiter
是非常类似的,multiCastChanel
的take
类似于eventEmiter
的once
,multiCastChanel
的put
类似于eventEmiter
的emit
。通俗的理解,saga 内 multiCastChanel 和 Chanel 最大的区别是,multiCastChanel 不能存 action,只能存 taker,能根据 action 的 type 判断是否执行 taker;chanel 可以缓存 action 和 taker,接收到 action 马上触发 taker,不会判断 type,类似于两个人对话的样子(单播)
一个简化版的 multiCastChanel 实现如下
源码中的 stdChanel 实现
上面代码中的
multicastChannel
和我们的简化版 chanel 原理是一样的。我们可以看到,stdChanel
是对multicastChannel
的put
方法进行了重写。只是对于非 saga 内置action
使用asap(() => { put(input); });
进行调用,这个asap
方法其实是 saga 内部调度系统的一个执行函数,它的作用是如果当前 saga 是空闲状态,则执行我们的回调;如果是挂起状态则将回调存进任务队列中。后面会专门介绍 saga 的调度系统。The text was updated successfully, but these errors were encountered: