Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

race condition for fast effects #15

Closed
joelgrus opened this issue Mar 29, 2016 · 4 comments
Closed

race condition for fast effects #15

joelgrus opened this issue Mar 29, 2016 · 4 comments

Comments

@joelgrus
Copy link

I want an Action to do nothing to the State and kick off an effect (whose result does something to the state). The problem is that in update I don't see a way not to set the state, even if I want it not to change. And if the effect returns very quickly, its signal gets rendered before the do-nothing change to the state. Here is a really contrived minimal example (the actual way I found this was more complicated and involved random numbers):

data Action = Increment | EffIncrement | Receive Int

type State = Int

update :: Action -> State -> EffModel State Action (console :: CONSOLE)
update Increment count =
  trace ("Increment: " ++ show count) \_ -> { state: count + 1, effects: [] }
update (Receive c) count =
  trace ("Receive: " ++ show c) \_ -> { state: c, effects: [] }
update EffIncrement count =
  -- I don't actually want to change the state here, but what can I do?
  trace "EffIncrement" \_ -> { state: count, effects: [pure $ Receive (count + 1)]}

view :: State -> Html Action
view count =
  div # do
    button ! onClick (const Increment) # text "Increment"
    span # text (show count)
    button ! onClick (const EffIncrement) # text "EffIncrement"

If I click on EffIncrement, the Receive (count + 1) signal gets rendered first. So if I click EffIncrement, what happens is the Receive 1 redraw happens before the state: 0 redraw, so the state gets updated but the UI appears not to. Then if you (e.g.) click Increment it goes up to 3.

A hacky solution is to artificially add a small delay to my fast effects, is there a better way?

(gist with full code: https://gist.github.com/joelgrus/f65b4106cdf32f4bc1109f0e1a0e0497)

@alexmingoia
Copy link
Owner

You should not be incrementing the count in your effect. Instead, the Receive action should include the amount to increment not the new count, because that count might be stale.

@joelgrus
Copy link
Author

I think what you're saying is change it to

  trace "EffIncrement" \_ -> { state: count, effects: [pure $ Increment]}

But that doesn't solve the problem. I think the state updates are happening in the right order, it's the renderings that get out of sequence? i.e., I think (modulo me not understanding purescript-signal that well) what's happening is something like

  • set state to count
  • send "render" signal with state = count (A)
  • receive "effectful" Increment action
  • increment state to count + 1
  • send "render" signal with state = count + 1 (B)
  • receive "render" signal with state = count + 1 (from B, out of order)
  • receive "render" signal with state = count (from A)

Does that make sense?

@alexmingoia
Copy link
Owner

Ah, I see the issue. This looks like a bug. I'm working on it.

@alexmingoia
Copy link
Owner

This is fixed in v2.0.1

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants