Tensegritics / ClojureDart

Clojure dialect for Flutter and Dart
1.44k stars 92 forks source link

cljd freezes when using `f/<!` on a Future in a cell #196

Closed straux closed 1 year ago

straux commented 1 year ago

Describe the bug

cljd freezes when using f/<! on a Future in a cell.

As a workaround, it is possible to declare the future in a first cell then retrieve its value in a second cell by taking it two times.

To Reproduce

This makes cljd freeze :

(def home
  (f/widget                                                                                                                                                                                                
    :let [v1 (f/$ (f/<! (Future.value 42)))
          v2 (f/$ (f/<! (Future.value 90)))
          v3 (f/$ (+ (f/<! v1)
                         (f/<! v2)))]
    :watch [val3 v3]
    (m/Scaffold
      .appBar (m/AppBar .title (m/Text "Cells test")))
    .body
    m/Center
    (m/Column)
    .children
    [(m/Text (str "Sum: " val3))]))

This works fine:

(def home
  (f/widget                                                                                                                                                                                                
    :let [v1 (f/$ (Future.value 42))
          v2 (f/$ (Future.value 90))
          v3 (f/$ (+ (f/<! (f/<! v1))
                     (f/<! (f/<! v2))))]
    :watch [val3 v3]
    (m/Scaffold
      .appBar (m/AppBar .title (m/Text "Cells test")))
    .body
    m/Center
    (m/Column)
    .children
    [(m/Text (str "Sum: " val3))]))

Expected behavior

Boths solutions should be valid and not freeze.

cgrand commented 1 year ago

It's a tough one, I can explain it but right now I have no idea how to fix it except of "don't do that".

TL;DR Don't create watchables in cells. (Don't perform side effects in cells either.)

The issue is that a new Future is created each time the cell is evaluated.

t=0 we create a cell which waits on a future t=1 the future created at t=0 is delivered, the cell is re-evaluated and ignores the Future new value because re-evaluating the cell created a dep on a new future. The cells wait on the newly created future. t=2 the future created at t=1 is delivered, the cell is re-evaluated and ignores the Future new value because re-evaluating the cell created a dep on a new future. The cells wait on the newly created future. ... t=N+1 the future created at t=N is delivered, the cell is re-evaluated and ignores the Future new value because re-evaluating the cell created a dep on a new future. The cells wait on the newly created future. ...

infinite loop, flutter not responding.

Take away: Don't create watchables in cells. (Don't perform side effects either.)

cgrand commented 1 year ago

I forgot to mention that this works:

(def home
  (f/widget                                                                    
    :let [v1 (Future.value 42)
          v2 (Future.value 90)
          v3 (f/$ (+ (f/<! v1) (f/<! v2)))]
    :watch [val3 v3]
    (m/Scaffold
      .appBar (m/AppBar .title (m/Text "Cells test")))
    .body
    m/Center
    (m/Column)
    .children
    [(m/Text (str "Sum: " val3))]))