Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Best way to listen for actions in saga: `while(true) take()` vs `while(take())` vs. `takeEvery()`

I've seen sagas listening for actions in 3 ways:

1. while(true) take()

function* onUserDetailsRequest() {
  while(true) {
    const { userId } = yield take(USER_DETAILS_REQUESTED);
    const response = yield call(fetchUserDetails, userId);
    put(USER_DETAILS_RECEIVED, response);
  }
}    

2. while(take())

function* onUserDetailsRequest() {
  while(yield take(USER_DETAILS_REQUESTED)) {
    const userId = yield select(userSelectorFn);
    const response = yield call(fetchUserDetails, userId);
    put(USER_DETAILS_RECEIVED, response);
  }
}    

3. takeEvery()

function* onUserDetailsRequest() {
  yield takeEvery(USER_DETAILS_REQUESTED, function* (action) {
    const { userId } = action;
    const response = yield call(fetchUserDetails, userId);
    put(USER_DETAILS_RECEIVED, response);
  }
}

What are the pros and cons of each? And in which scenarios should we use one over another?

like image 789
Ali Saeed Avatar asked Dec 20 '17 15:12

Ali Saeed


People also ask

What is TakeEvery and takeLatest in Redux-saga?

takeEvery - enables the use of several fetchData objects at the same time. At a given moment, we can start a new fetchData task while there are still one or more previous fetchData tasks which have not yet terminated. takeLatest - Only one fetchData task can be active at any given moment.

What is the main task of TakeEvery?

TakeEvery is a high-level API built using Take and Fork. TakeEvery doesn't handle out of order responses from tasks. There is no guarantee that the tasks will terminate in the same order they were started. To handle out of order responses, you may consider TakeLatest.

What is takeLatest?

Unlike takeEvery , takeLatest allows only one fetchData task to run at any moment. And it will be the latest started task. If a previous task is still running when another fetchData task is started, the previous task will be automatically cancelled.

Which Effect causes the middleware by telling them to wait for a specific action on the store?

The take is just like call and put we saw earlier. It creates another command object that tells the middleware to wait for a specific action. The resulting behavior of the call Effect is the same as when the middleware suspends the Generator until a Promise resolves.

What is the difference between takeevery and takesagawithfork?

For takeEverySaga and takeSagaWithFork, however, a new task was forked everytime it recieved a testTakeEvery action, so they were sleeping in their own task "thread" and so new action won't get missed. Thus, takeEvery is essentially the same as while (true) + take + fork.

Why does the takesaga task ignore the second testtake action?

The task of takeSaga is sleeping when the second testTake action dispatched therefore the takeSaga simply ignored the second testTake action. For takeEverySaga and takeSagaWithFork, however, a new task was forked everytime it recieved a testTakeEvery action, so they were sleeping in their own task "thread" and so new action won't get missed.

What is the difference between Spawn () and takeevery () in Saga?

Spawns a saga on each action dispatched to the Store that matches pattern. args: Array - arguments to be passed to the started task. takeEvery will add the incoming action to the argument list (i.e. the action will be the last argument provided to saga)

What is takeevery and takelatest in Redux-Saga?

Think of takeEvery and takeLatest as helper funtions on top of the lower level API of redux-saga which are wrapping internal operations such as spawning tasks when specific actions are dispatched to the Store. Calling them spawns a saga on each action dispatched to the Store that matches pattern.


1 Answers

To clearify @AlexM's answer with code.

cat test.js

const { createStore, applyMiddleware } =require('redux')
const createSagaMiddleware =require('redux-saga').default
const { takeEvery ,take,fork}=require('redux-saga/effects') 
const {delay} =require('redux-saga')
const sagaMiddleware = createSagaMiddleware()
const reducer=(state=[],action)=>{return [...state,action.type];}
const store = createStore(
    reducer,
    applyMiddleware(sagaMiddleware)
)
function* takeSaga() {
  while(true){
    const action=yield take('testTake')
    console.log(action)
    yield delay(1000)
  }
}

function* takeEverySaga() {
    yield takeEvery('testTakeEvery',function* (action){
        console.log(action)
        yield delay(1000)
    })
}

function* takeSagaWithFork() {
    while(true){
      const action=yield take('testTakeWithFork')
      yield fork(function*(){
        console.log(action)
        yield delay(1000)
      })
    }
}

sagaMiddleware.run(takeSaga)
sagaMiddleware.run(takeEverySaga)
sagaMiddleware.run(takeSagaWithFork)

const main=async ()=>{
    store.dispatch({type: 'testTake'})
    store.dispatch({type: 'testTake'})
    store.dispatch({type: 'testTakeEvery'})
    store.dispatch({type: 'testTakeEvery'})
    store.dispatch({type: 'testTakeWithFork'})
    store.dispatch({type: 'testTakeWithFork'})
}

main();

run the above code with node test.js will output

{ type: 'testTake' }
{ type: 'testTakeEvery' }
{ type: 'testTakeEvery' }
{ type: 'testTakeWithFork' }
{ type: 'testTakeWithFork' }

Do you see the difference? The task of takeSaga is sleeping when the second testTake action dispatched therefore the takeSaga simply ignored the second testTake action. For takeEverySaga and takeSagaWithFork, however, a new task was forked everytime it recieved a testTakeEvery action, so they were sleeping in their own task "thread" and so new action won't get missed. Thus, takeEvery is essentially the same as while(true)+take+fork.

like image 151
Benjamin Avatar answered Sep 28 '22 06:09

Benjamin