Redux 通过中间件的机制,可以解耦业务逻辑,更加灵活的处理异步的操作。
在实际使用的过程中,每个 中间件的 call 函数中,大多数情况需要调用 next(action), 否则中间件的链条断了,后面的中间件和Reducer就不执行了。
那么 next(action) 什么时候调用呢? 这个问题困扰了我很久, 于是我做了下面的实验。
首先,我定义了3个 Action:
enum Actions { action1, action2, action3 }
分别处理Action 的 Reducer:
int reducer(int state, dynamic action) {
if (action == Actions.action1) {
print('Reducer action1');
} else if (action == Actions.action2) {
print('Reducer action2');
} else if (action == Actions.action3) {
print('Reducer action3');
}
return state;
}
下面是重点: 中间件。
中间件的执行过程
void middleware1(Store<int> store, dynamic action, NextDispatcher next) {
print('Middleware 1 A');
next(action);
print('Middleware 1 B');
}
void middleware2(Store<int> store, dynamic action, NextDispatcher next) {
print('Middleware 2 A');
next(action);
print('Middleware 2 B');
}
void middleware3(Store<int> store, dynamic action, NextDispatcher next) {
print('Middleware 3 A');
next(action);
print('Middleware 3 B');
}
执行上面的代码, 当发出 dispatch Action1 时, 执行的结果是:
flutter: Middleware 1 A
flutter: Middleware 2 A
flutter: Middleware 3 A
flutter: Reducer action1
flutter: Middleware 3 B
flutter: Middleware 2 B
flutter: Middleware 1 B
结论:
Redux 当发出 Action 时, 会调用第一个中间件的 call 方法, 然后由中间件依次调用 next(action) 来执行所有加入到 middleware 数组里面的中间件,最后一个中间件会调用 reducer。
如果想要拦截某个 Action, 就在中间件的 call 方法中不调用 next(action) , 这样 reducer 就不会执行了。
中间件中发出其他 Action
经常出现的场景,在中间件中,发出其他的Action 进行状态控制, 比如在网络请求中间件中, 收到数据后发送 Action 更改状态。
修改上面 中间件部分 middleware2 的代码, 在 调用 next(action) 前面 dispatch 发出Action2。
void middleware2(Store<int> store, dynamic action, NextDispatcher next) {
print('Middleware 2 A');
if (action == Actions.action1) {
print('Dispatch action2');
store.dispatch(Actions.action2);
print('Dispatch action2 end');
}
next(action);
print('Middleware 2 B');
}
执行上面的代码, 当发出 dispatch Action1 时, 执行的结果是:
flutter: Middleware 1 A
flutter: Middleware 2 A
flutter: Dispatch action2
flutter: Middleware 1 A
flutter: Middleware 2 A
flutter: Middleware 3 A
flutter: Reducer action2
flutter: Middleware 3 B
flutter: Middleware 2 B
flutter: Middleware 1 B
flutter: Dispatch action2 end
flutter: Middleware 3 A
flutter: Reducer action1
flutter: Middleware 3 B
flutter: Middleware 2 B
flutter: Middleware 1 B
结论:
在中间件中,调用 dispatch 发送其他Action, 会递归先处理新发出的 Action, 所以这时候 next(action) 写在哪里就很重要了。
如果 reducer 执行时,需要依赖新发出的 Action 对状态的改变, 那就要在 next(action) 前面 dispatch 发出新的 Action。
如果 dispatch 新的 Action 需要依赖于本次处理的 Action 对状态的改变,那就要在 next(action) 后面 dispatch 发出新的 Action, 这种情况更常见一些。
异步逻辑会改变中间件的执行顺序
在 Dart 语言中, 可以使用 async/await 将异步的代码优雅的用同步的方式来表示。所以使用 await 调用的方法会改变代码的执行顺序。
在 Redux 中, 中间件是处理异步操作的好地方, 在中间件中使用 await 调用 async 的方法, 也会改变中间件的执行顺序。
为了方便实验,我在代码中加入了一个空的 async 方法:
Future<Null> doNothing() async {}
修改 middleware2 的代码, await 调用 doNothing 方法。
Future<Null> middleware2(Store<int> store, dynamic action, NextDispatcher next) async {
print('Middleware 2 A');
if (action == Actions.action1) {
await doNothing();
print('Dispatch action2');
store.dispatch(Actions.action2);
print('Dispatch action2 end');
}
next(action);
print('Middleware 2 B');
}
执行上面的代码, 当发出 dispatch Action1 时, 执行的结果是:
flutter: Middleware 1 A
flutter: Middleware 2 A
flutter: Middleware 1 B
flutter: Dispatch action2
flutter: Middleware 1 A
flutter: Middleware 2 A
flutter: Middleware 3 A
flutter: Reducer action2
flutter: Middleware 3 B
flutter: Middleware 2 B
flutter: Middleware 1 B
flutter: Dispatch action2 end
flutter: Middleware 3 A
flutter: Reducer action1
flutter: Middleware 3 B
flutter: Middleware 2 B
为了更好的说明问题, 再次修改 middleware2 的代码, 将 await 调用 doNothing 方法和 dispatch 新的Action 方法 放到了 next(action) 的后面。
Future<Null> middleware2(Store<int> store, dynamic action, NextDispatcher next) async {
print('Middleware 2 A');
next(action);
if (action == Actions.action1) {
await doNothing();
print('Dispatch action3');
store.dispatch(Actions.action3);
print('Dispatch action3 end');
}
print('Middleware 2 B');
}
执行上面的代码, 当发出 dispatch Action1 时, 执行的结果是:
flutter: Middleware 1 A
flutter: Middleware 2 A
flutter: Middleware 3 A
flutter: Reducer action1
flutter: Middleware 3 B
flutter: Middleware 1 B
flutter: Dispatch action3
flutter: Middleware 1 A
flutter: Middleware 2 A
flutter: Middleware 3 A
flutter: Reducer action3
flutter: Middleware 3 B
flutter: Middleware 2 B
flutter: Middleware 1 B
flutter: Dispatch action3 end
flutter: Middleware 2 B
结论:
需要注意,如果在 调用 next(action) 之前, 使用 await 调用, 会阻塞住整个中间件的链式调用,然后在 await 位置立刻返回, 造成不符合预期的结果。
如果要在中间件中使用异步操作, 需要放在 next(action) 之后, 这样不会影响其他的中间件调用, 用可以把异步的逻辑放到最后执行。
特别注意
本代码是在 Flutter 0.5.6 和 Dart 2.0.0-dev.63.0 环境下的测试结果, 如果在 Dart 2.0.0-dev.58.0 环境下, 运行的结果和上面的结果是不同的。
这是因为 Dart 语言 改变了 async/await 的执行方式, 我认为新版本的修改是合理的。