loading...

Polling with redux saga

vikasgarghb profile image Vikas Garg Updated on ・2 min read

I had been working on a solution to wait for a result to be available to update the results on a UI upon a user action. We are using react and redux. Looking at the other solutions available everywhere using redux-saga, they were all using an infinite loop and using specific redux-saga provided events to halt the polling. This made me think of making use of redux store and reducers to implement polling. So, this is my attempt to solve this problem.

What is polling?

Polling in general term means checking the status of something OR making repeated calls for a response. Eg. news ticker; twitter feeds etc.

Redux Saga

Redux Saga -- It uses an ES6 generator functions which are similar to async/await syntax.

Let's get to the business

For this, we are going to use a dummy service which have following APIs,

- POST /job -- Creates a job.
- GET /:jobId/status -- Returns the status of the job

Note: Not going into the details on the implementation of the APIs. Our focus will be on redux-saga implementation.

redux-saga

Let's see the implementation

export function* createJob(action) {
  const { payload } = action;

  yield post({
    url: '/job',
    onSuccess: ON_SUCCESS,
    onError: ON_ERROR,
    data: payload,
  });
}

export function* startPollingForStatus() {
  const job = yield select(state => getJob(state));
  yield put(getStatus(job.id));
}

export function* getStatus(action) {
  const { jobId } = action;

  yield get({
    url: `/${jobId}/status`,
    onSuccess: ON_SUCCESS_GET_STATUS,
    onError: ON_ERROR,
  });
}

export function* isJobComplete() {
  const job = yield select(state => getJob(state));
  if (job.status === 'complete') {
    return yield put(jobIsComplete(job.guid));
  }

  if (['created', 'processing'].includes(job.status)) {
    yield delay(4000);
    return yield put(getStatus(job.id));
  }

  yield put({
    type: ON_ERROR,
  });
}

export function* jobIsComplete() {
  return 'success';
}

export default function* root() {
  return yield [
    takeLatest(ON_CREATE_JOB, createJob),
    takeLatest(ON_GET_STATUS, getStatus),
    takeLatest(ON_SUCCESS, startPollingForStatus),
    takeLatest(ON_SUCCESS_GET_STATUS, isJobComplete),
    takeLatest(ON_ERROR, onError),
  ];
}

This might look a bit too much but let's break it down.

startPollingForStatus
As soon as the job is created and is returned successfully, we start the polling. As a result of the creation job, we have the returned job stored in redux store and then use the job id to fetch its status.

isJobComplete
Upon successful return of the status, we check for different statuses: created, complete and processing. If the status is not complete, we want to continue polling. So, we make use of delay function provided by redux-saga. This will delay for given time (in ms) and then resume the polling. If the status is complete, we will halt the polling and do something else to show the user that the job is complete.

Conclusion

This makes use of redux store and state and get us out of using an infinite loop to poll for updates. Let me know in comments if you see any issues with this approach.

Plan to add some samples for tests and github link for working solution. :)

UPDATE: Code can be found here

Discussion

pic
Editor guide