Redux 通過中間件的機制,可以解耦業(yè)務(wù)邏輯韵吨,更加靈活的處理異步的操作入挣。
在實際使用的過程中,每個 中間件的 call 函數(shù)中鲫售,大多數(shù)情況需要調(diào)用 next(action)共螺, 否則中間件的鏈條斷了,后面的中間件和Reducer就不執(zhí)行了情竹。
那么 next(action) 什么時候調(diào)用呢藐不? 這個問題困擾了我很久, 于是我做了下面的實驗秦效。
首先雏蛮,我定義了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;
}
下面是重點: 中間件。
中間件的執(zhí)行過程
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');
}
執(zhí)行上面的代碼棉安, 當發(fā)出 dispatch Action1 時底扳, 執(zhí)行的結(jié)果是:
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
結(jié)論:
Redux 當發(fā)出 Action 時铸抑, 會調(diào)用第一個中間件的 call 方法贡耽, 然后由中間件依次調(diào)用 next(action) 來執(zhí)行所有加入到 middleware 數(shù)組里面的中間件,最后一個中間件會調(diào)用 reducer鹊汛。
如果想要攔截某個 Action蒲赂, 就在中間件的 call 方法中不調(diào)用 next(action) , 這樣 reducer 就不會執(zhí)行了刁憋。
中間件中發(fā)出其他 Action
經(jīng)常出現(xiàn)的場景滥嘴,在中間件中,發(fā)出其他的Action 進行狀態(tài)控制至耻, 比如在網(wǎng)絡(luò)請求中間件中若皱, 收到數(shù)據(jù)后發(fā)送 Action 更改狀態(tài)镊叁。
修改上面 中間件部分 middleware2 的代碼, 在 調(diào)用 next(action) 前面 dispatch 發(fā)出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');
}
執(zhí)行上面的代碼走触, 當發(fā)出 dispatch Action1 時晦譬, 執(zhí)行的結(jié)果是:
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
結(jié)論:
在中間件中,調(diào)用 dispatch 發(fā)送其他Action互广, 會遞歸先處理新發(fā)出的 Action敛腌, 所以這時候 next(action) 寫在哪里就很重要了。
如果 reducer 執(zhí)行時惫皱,需要依賴新發(fā)出的 Action 對狀態(tài)的改變像樊, 那就要在 next(action) 前面 dispatch 發(fā)出新的 Action锻煌。
如果 dispatch 新的 Action 需要依賴于本次處理的 Action 對狀態(tài)的改變刽肠,那就要在 next(action) 后面 dispatch 發(fā)出新的 Action茂契, 這種情況更常見一些撩鹿。
異步邏輯會改變中間件的執(zhí)行順序
在 Dart 語言中唇撬, 可以使用 async/await 將異步的代碼優(yōu)雅的用同步的方式來表示埠胖。所以使用 await 調(diào)用的方法會改變代碼的執(zhí)行順序析既。
在 Redux 中笆焰, 中間件是處理異步操作的好地方韩脑, 在中間件中使用 await 調(diào)用 async 的方法氢妈, 也會改變中間件的執(zhí)行順序。
為了方便實驗段多,我在代碼中加入了一個空的 async 方法:
Future<Null> doNothing() async {}
修改 middleware2 的代碼首量, await 調(diào)用 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');
}
執(zhí)行上面的代碼进苍, 當發(fā)出 dispatch Action1 時加缘, 執(zhí)行的結(jié)果是:
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 調(diào)用 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');
}
執(zhí)行上面的代碼, 當發(fā)出 dispatch Action1 時杠人, 執(zhí)行的結(jié)果是:
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
結(jié)論:
需要注意勋乾,如果在 調(diào)用 next(action) 之前, 使用 await 調(diào)用嗡善, 會阻塞住整個中間件的鏈式調(diào)用辑莫,然后在 await 位置立刻返回, 造成不符合預(yù)期的結(jié)果罩引。
如果要在中間件中使用異步操作各吨, 需要放在 next(action) 之后, 這樣不會影響其他的中間件調(diào)用袁铐, 用可以把異步的邏輯放到最后執(zhí)行揭蜒。
特別注意
本代碼是在 Flutter 0.5.6 和 Dart 2.0.0-dev.63.0 環(huán)境下的測試結(jié)果横浑, 如果在 Dart 2.0.0-dev.58.0 環(huán)境下, 運行的結(jié)果和上面的結(jié)果是不同的屉更。
這是因為 Dart 語言 改變了 async/await 的執(zhí)行方式伪嫁, 我認為新版本的修改是合理的。