Introduction
In this section, you should learn
- Definition of #redux#saga
- Watchers in Saga are closely related to observer pattern that monitor actions as events
- Subroutines in Saga are implementations of iterator pattern that manage async operation in synchronous style
- Naming convention in Saga programming
- Controlling concurrency and blocking flow
- Triggering single subroutine in multiple watchers for the sake of while-loop
- Bearing buffer action regulation in batch processing, like producer-consumer-model
- Interrupting asynchronous subroutine with graceful procedure.
Enrich yourself daily
Playing games, as well as reading game magazines, is a good way to learn and experience Saga; whenever you see a feature, try to guess the flow control behind. In #Zelda
- HpSaga manipulates the HP mutation of getting fire, battling with monsters, etc
- WeaponSaga conducts the durability of your current stored weapon
- WorldSaga controls the existence of various objects
- GravitySaga direct how you and other monsters fall
- BattleSaga takes APPROACH_TO_MONSTER action, and triggers the battle AI subroutine
- Each MissionSaga and ShrineSaga observes selected actions closely
- etc
Definition
Saga is observer, it triggers some side-effects when an action is dispatched
// old days
monster.on('killed', (event) => {
event.killer.weapon.durability(-1);
event.monster.vanish();
event.world.add(event.monster.rewards());
event.killer.bloody(+1);
});
player.on('killed', (event) => {
if (event.player.rebirth > 0){
event.player.rebirth--;
event.player.health = 0.5 * event.player.max_health;
} else {
event.gameover();
}
});
//new days
function* weaponSaga(){
while (true){
const { monster } = yield take (MONSTER_KILLED);
yield put (WEAPON_DAMAGED, monster.hp * 0.2);
}
}
function* lifeSaga(){
while (true){
const factor = yield race [MONSTER_DAMAGE, FALLING, BURNING, FREEZING, EATING];
var currentHP = yield select((state) => state.currentHP);
switch (factor){
// manipulate currentHP
}
if (currentHP < 0){
const rebirth = yield select((state) => state.rebirth);
if (rebirth > 0){
yield put(REBIRTH);
} else {
yield put(GAMEOVER);
// NavigationAction.go('/gameover')
}
} else {
yield put(UPDATE_HP, currentHP)
}
}
}
Each Saga has watcher
function* watchVerbNoun(){
while (true){
const {params} = yield take(actions.NOUN_VERB);
yield fork(loadSth, params)
}
}
export default function* root() {
yield all([
fork(watchVerbNoun),
...
])
}
In some cases, like prefetch and refresh, you may find a do-while-loop could reduce coding, yet elegant coder still writes while-loop. Why?
Each Saga has watcher, followed by subroutine
Examples
- Watcher and subroutine
- Concurrent or blocking
- Prefetch and refresh
- Buffer action or event
- Cancellable counter