******若是说 model,namespace,connect,dispatch,action,reducer 这些是 dva 的基石,那 dva 的精髓就体如今于 effect。*******javascript
那么 effect 究竟是什么呢 ?effect 是一个 dva 语境中的名词。和 reducers 相似,咱们也能够在 dva model 中定义一个 effects
成员。html
export default { namespace: 'some_namespace', state: {}, effects: { // 定义 effects 成员 'someEffect': function*() {}, 'someOtherEffect': function*() {}, // ... }, reducers: { // ... }, }
局部上看 effect 就是一个一个的生成器函数generator function。宏观上看,effect 是一层中间件。
在上一章中 action 被 dispatch 以后就可以 直接 到达 reducer。为了保证 reducer 的纯粹性,但同时又可以处理反作用,就须要打破「直接」这个特性。effect 充当了这么一个中间层,当 action 被 dispatch 以后,会先到达 effect 处理反作用,而后该 effect 最终会促使新的 action 发送出去,这个新的 action 可能被其余的 effect 再捕获继续处理,也可能被 reducer 捕获并结束,不管怎样,最终处理逻辑的终点都将是 reducer。java
在上一章节中,咱们知道 action.type 的构造是 namespace 名称
+ /
+ reducer 名称
,事实上 action.type 也能够是 namespace 名称
+ /
+ effect 名称
。对于视图层来说,其实并不会感知 effect 和 reducer 的区别。视图层只是经过 action 描述想作什么,至于这个 action 以后是直接被 reducer 处理仍是经过 effect 再到 reducer,视图层并不感知,也不该该关心。这样咱们就作到了数据逻辑和视图逻辑的分离处理。express
咱们再解释为何 generator function 能够用来处理异步逻辑。其实 generator function 处理异步逻辑并非 dva 的专利,在许多 js 框架中都用到了,最著名的就是 co。使用 generator function 处理异步也不是对语言特性的乱用,而是说 generator function 自然地就具有处理异步的特质。dva 中一个典型的 effect 的写法是:redux
getData: function* ({ payload }, { call, put }) { const data = yield call(SomeService.getEndpointData, payload, 'maybeSomeOtherParams'); yield put({ type: 'getData_success', payload: data }); }
先说结论:当这个 generator function 被执行时,执行的流程 看上去 会是同步的!入参有两个对象,第一个对象就是匹配这个 effect 的 action 对象,所以能够取到约定的 payload 这个字段,第二个对象是 effect 原语集,
其中 , 最为经常使用。generator function 入参中的两个对象都是在运行时由 dva 注入到 generator function 中的。 实际上是一个函数,和 关键字配合使用处理异步逻辑,call 第一个参数是一个函数,要求函数返回 Promise,
以后的参数是该函数调用时的入参。yield call 调用后就阻塞了,Promise 被解析后,获得异步调用的结果,存储到 data 中,而后程序才能继续进行。看到下面一行又执行了 put。
也是一个函数,put 和 配合使用,用来派发一个 action,和 dispatch 的功能 如出一辙!只不过是在 effect 函数中使用而已。callputcallyieldputyield
注意: 派发的 action 若是是为了触发 同 model 中的其余 effect/reducer 执行,不须要指定 namespace 名称。
yield put
异步的实质是事件发生促使程序的执行点来回跳转。咱们使用 callback 本质上是描述跳转的一种手段。generator function 并无改变异步的本质,只是改变了描述的方式,使得程序看起来像是同步同样。框架
一个 generator function 在执行时有 两方。一方是 generator function 自己,另外一方是 generator function 的句柄持有者,而这通常都是框架所持有。咱们姑且称这个句柄为 genStub。当框架调用 genStub.next() 时,generator function 会执行到下一个 yield
而后暂停,并把 yield 后面表达式的计算值返还给框架,同时把程序执行权交给框架。框架拿到值后作处理,好比就是异步处理,处理结束拿到结果,再次调用 genStub.next(),返还值给 generator function 同时驱动它恢复执行。当恢复执行时,你能够认为 返回的处理结果会总体替换 yield <expression>
,而后程序继续执行到下一个 yield。异步
yield 这个单词用在这里特别形象:yield 自己有「让步」的意思,也有「产出」的意思。函数
「generator function yield 到外部的值」和「外部返还给 generator function 的值」不是一回事!!!spa
generator function 定义了流程,并在每次 yield 节点上报想作的事情。而异步的真正执行逻辑由 generator function 句柄的持有者代为执行。对应到 dva 上也是同样的。拿 call 作例子,call 实际上是一个特别简单的函数。call 的返回值只是一个 plain javascript object:3d
{
CALL: { fn: SomeService.getEndpointData, args: [payload, 'maybeSomeOtherParams'] } }
咱们经过 call 向 dva 描述了想作的事情:请帮我执行这个函数,Promise 解析后通知我继续执行,并把 Promise 的解析值返回给我。