Isaac
Isaac

Reputation: 12884

Unable to understand non-blocking calls in Redux-Saga

I'm trying to understand Non-blocking calls section of redux-saga documentation and stuck with the below code

import { fork, call, take, put } from 'redux-saga/effects'
import Api from '...'

function* authorize(user, password) {
  try {
    const token = yield call(Api.authorize, user, password)
    yield put({type: 'LOGIN_SUCCESS', token})
    yield call(Api.storeItem, {token})
  } catch(error) {
    yield put({type: 'LOGIN_ERROR', error})
  }
}

function* loginFlow() {
  while (true) {
    const {user, password} = yield take('LOGIN_REQUEST')
    yield fork(authorize, user, password)
    yield take(['LOGOUT', 'LOGIN_ERROR'])
    yield call(Api.clearItem, 'token')
  }
}

The explanation says

If the authorize fails before the user logs out, it will dispatch a LOGIN_ERROR action, then terminate. So loginFlow will take the LOGIN_ERROR before the LOGOUT then it will enter in a another while iteration and will wait for the next LOGIN_REQUEST action.

I can't really understand the statement So loginFlow will take the LOGIN_ERROR before the LOGOUT then it will enter in a another while iteration and will wait for the next LOGIN_REQUEST action.

Can anyone explain the relation between yield put({type: 'LOGIN_ERROR', error}) and yield take(['LOGOUT', 'LOGIN_ERROR'])?

Upvotes: 1

Views: 2536

Answers (3)

Marvin
Marvin

Reputation: 754

NON-BLOCKING is a Synchronous flow, which means that the Saga disobey the block of codes and simultaneously read the lines within the parent block, the reason why it was called Non-Blocking

BLOCKING call means that the Saga yielded an Effect and await the previous block before it continuous for the next block execution and basically it was called Blocking

To your case, you just misunderstood the documentation of redux-saga but it was just about like this

import {call, cancel, join, take, put} from "redux-saga/effects"

function* saga() {
  yield take(ACTION)              // Blocking: will wait for the action
  yield call(ApiFn, ...args)      // Blocking: will wait for ApiFn (If ApiFn returns a Promise)
  yield call(otherSaga, ...args)  // Blocking: will wait for otherSaga to terminate

  yield put(...)                   // Non-Blocking: will dispatch within internal scheduler

  const task = yield fork(otherSaga, ...args)  // Non-blocking: will not wait for otherSaga
  yield cancel(task)                           // Non-blocking: will resume immediately
  // or
  yield join(task)                              // Blocking: will wait for the task to terminate
}

For more info just read about the documentation itself of Redux-Saga

Upvotes: 0

L To The J
L To The J

Reputation: 53

To simply explain:

  • yield put({type: 'LOGIN_ERROR',error}) = dispatch action indicating that there is a LOGIN_ERROR.
  • yield take(['LOGOUT', 'LOGIN_ERROR']) = wait until there is an either LOGOUT or LOGIN_ERROR action to be dispatched and only continue executing the next line which is yield call(Api.clearItem, 'token') if there is a LOGOUT or LOGIN_ERROR

Upvotes: 1

Aleksey L.
Aleksey L.

Reputation: 37986

yield put({type: 'LOGIN_ERROR', error}) - dispatches action of type LOGIN_ERROR.

yield take(['LOGOUT', 'LOGIN_ERROR']) suspends generator function (loginFlow) execution until action of type LOGOUT or LOGIN_ERROR is dispatched.

Upvotes: 2

Related Questions