question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Inconsistent Blocking Actions

See original GitHub issue

The first example in the README seems to be broken. It is supposed to wait for INCREMENT_ASYNC actions, then delay them by 1 second, and then pass an INCREMENT_COUNTER action on, but it misses any extra actions fired in that 1 second delay.

import { take, put } from 'redux-saga'
// sagas/index.js

function* incrementAsync() {

  while(true) {

    // wait for each INCREMENT_ASYNC action  
    const nextAction = yield take(INCREMENT_ASYNC)

    // delay is a sample function
    // return a Promise that resolves after (ms) milliseconds
    yield delay(1000)

    // dispatch INCREMENT_COUNTER
    yield put( increment() )
  }

}

export default [incrementAsync]

This problem exists in any saga that makes a blocking call (delay, call, join, put etc.). My initial recommendation would therefore be: If you don’t want race conditions, you must never make sagas that contain blocking calls. This makes sagas pretty much useless though, unless you work on the basis of always having a top level saga that looks like while (true) yield fork(yield take('a'));.

My recommendation is to do one of two things:

Buffer actions

One option is to buffer actions so that take just reads the next item of the buffer. This has a couple of issues:

  1. It prevents parallelism. In the above example, doing dispatch(incrementAsync());dispatch(incrementAsync());dispatch(incrementAsync()); would result in a 3 second delay (1 second * 3) rather than all running in parallel and taking about 1 second.
  2. You don’t know in advance when or if a saga will attempt to take a given action type, so you are forced to buffer all actions indefinitely.

Subscribe at the top level

I imagine that 90% of sagas look like:

export default function* mySaga() {
  while (true) {
    let action = yield take(MY_ACTION_NAME);
    // ... business logic here ...
  }
}

So, with that in mind, why not just have that be re-written as:

function* mySaga(action) {
  // ... business logic here ...
}
export default createSaga(MY_ACTION_NAME, mySaga);

Saga could then dispatch actions matching MY_ACTION_NAME in parallel.

What you loose seems to be the ability to “hide” state inside sagas, but I don’t think you should be putting state inside your sagas. Keep your state in the store (updated via reducers).

Issue Analytics

  • State:closed
  • Created 8 years ago
  • Comments:13 (9 by maintainers)

github_iconTop GitHub Comments

1reaction
yelouaficommented, Feb 17, 2016

@ForbesLindesay the new release 0.8.0 introduced the helper functions takeEvery and takeLatest which offers a safer way for new users. The helpers are introduced early in the docs (README, beginner tutorial and basics section)

take is not introduced until the advanced section And Immediately followed by a section on Non blocking calls. This section illustrates the pitfalls of using take with call, and shows the right way to do non blocking calls when using take to implement complex flows.

I also started refactoring the examples in the repo to use the helpers.

Do you think this would be sufficient to prevent the mentioned race issues ? New users are exposed only to the helper functions. And advanced users are warned about the use of take with blocking calls.

Also you may find here an interesting case of how ‘missing’ the actions allows implementing some useful patterns like throttling and debouncing.

1reaction
ForbesLindesaycommented, Feb 8, 2016

I think there are interesting ideas here, and if we can find ways to enforce that sagas are always “safe”, it will be great. As it stands, most examples in saga’s own documentation contain race conditions. I think it is safe to say if the docs for a library contain race conditions, so will real world programmes that use that library.

One possible alternative solution, instead of:

import { put, subscribe } from 'redux-saga'
// sagas/index.js

const incrementAsync = subscribe(['INCREMENT_ASYNC'], function* (take) {

  while(true) {

    // wait for each INCREMENT_ASYNC action  
    const nextAction = yield take()

    // delay is a sample function
    // return a Promise that resolves after (ms) milliseconds
    yield delay(1000)

    // dispatch INCREMENT_COUNTER
    yield put( increment() )
  }

});

export default [incrementAsync]

You explicitly specify up-font which action types you are interested in, which are then buffered until you take them. I don’t think this removes any of the flexibility you would want, but it prevents the current footgun.

Read more comments on GitHub >

github_iconTop Results From Across the Web

Inconsistent interactions when blocking allies die before ...
Inconsistent interactions when blocking allies die before striking? ... Example 1: Glimpse Beyond is cast on a blocking unit. It dies and leaves ......
Read more >
CSCvp97959 - Bridge Assurance Inconsistent ... - Cisco Bug
Cisco Bug: CSCvp97959 - Bridge Assurance Inconsistent blocking/unblocking action not in `show logging log` Symptom: If one interface is placed ...
Read more >
Resolve blocking problem caused by lock escalation - SQL ...
This article describes how to determine whether lock escalation is causing blocking and how to resolve the problem.
Read more >
WAFL inconsistent: inconsistent user data block at VBN
An inconsistent block is reported on the performance tier. Example: Tue Jul 20 09:58:02 +0200 [node01: wafl_exempt05: wafl.raid.incons.
Read more >
3012630 - Error message Check Storage Inconsistent when ...
You want to block or close a Check Storage but the Save action fails and the error message 'Check storage inconsistent' appears.
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found