GenieFramework / Stipple.jl

The reactive UI library for interactive data applications with pure Julia.
MIT License
318 stars 27 forks source link

0.25.20 breaks asynchronous tasks in reactive code blocks #176

Open PGimenez opened 1 year ago

PGimenez commented 1 year ago

I have this code that runs a @task in which a counter gets increased each second. It worked in 0.25.15, but after upgrading to 0.20.20 it stops working.

using GenieFramework
@genietools 

@handlers begin
    p = @task 1+1
    @in x = 0.00
    @onchange isready begin
        if !istaskstarted(p) || istaskdone(p) 
            p = @task begin
                println("Task started")
                while x <= 100
                    x = x + 1
                    sleep(1)
                end
            end
            schedule(p)
        end
    end
end

function ui()
        bignumber("Counter", :x)
end

@page("/", ui)
Server.isrunning() || Server.up()

this is the error I'm getting on 0.20.25 and Julia 1.8.5

┌ Error: 2023-02-15 20:56:50 Error attempting to invoke handler 3 for field Reactive{Bool}(Observable(true), 1, false, false, "") with value true
└ @ Stipple ~/.julia/packages/Stipple/Ird4L/src/stipple/mutators.jl:36
┌ Error: 2023-02-15 20:56:50 UndefVarError(:p)
└ @ Stipple ~/.julia/packages/Stipple/Ird4L/src/stipple/mutators.jl:37
AbhimanyuAryan commented 1 year ago

I think @hhaensel can look at it. Can you check if it work on master with changes helmut made for kw. Not sure I understand the error

hhaensel commented 1 year ago

I know what happened. You are using a local variable in handlers which I had not thought of when I generalised the Reactive API. I shall address this in a future release. Meanwhile an easy work around is to define a private field instead of a local variable:

@app begin
    @private p = @task 1+1
    @in x = 0.00
    @onchange isready begin
        if !istaskstarted(p) || istaskdone(p) 
            p = @task begin
                println("Task started")
                while x <= 100
                    x = x + 1
                    sleep(1)
                end
            end
            schedule(p)
        end
    end
end

Note, that I have used @appinstead of @handlers which clears the variable cache, so that you can rename variables without having old names trailing in the model.

hhaensel commented 1 year ago

I also enhanced @handlers so that you can also declare non_reactive fields:

@app begin
    @private non_reactive p = @task 1+1
    @in x = 0.00
    @onchange isready begin
        if !istaskstarted(p) || istaskdone(p) 
            p = @task begin
                println("Task started")
                while x <= 100
                    x = x + 1
                    sleep(1)
                end
            end
            schedule(p)
        end
    end
end
essenciary commented 1 year ago

Should the official recommendation (and demos) be to always use @app instead of @handlers? Or why don't we replace @app with @handlers at code level so all codebases get automatically migrated to the @app features?

hhaensel commented 1 year ago

I explained in detail in #159.

In my eyes @app is the new standard, it combines both field and handler definition and clears both storages before setting the new values. If you are adapting old code the quickest way is to replace the old @reactive statement with the respective @vars statement and to define handlers separately by @handlers, which then does not clear the REACTIVE_STORAGE, e.g. from

@reactive! mutable struct TreeDemo <: ReactiveModel
    d::R{Dict{String, Any}} = deepcopy(testdict)
    tree::R{Vector{Dict{Symbol, Any}}} = [dict_tree(testdict)]

    tree_selected::R{String} = ""
    tree_ticked::R{Vector{String}} = String[]
    tree_expanded::R{Vector{String}} = String[]
end

function handlers(model)
    on(model.isready) do isready
        isready && push!(model)
    end

    model
end

to

@appname TreeDemo

@vars begin
    d = deepcopy(testdict)
    tree = [dict_tree(testdict)]

    tree_selected = ""
    tree_ticked = String[]
    tree_expanded = String[]
end

@handlers
    onchange isready begin
        isready && @push
    end
end
PGimenez commented 1 year ago

@hhaensel did you make any change about this? Are variables non-reactive by default now?