Race condition in online plotting with Makie

I’m time stepping a problem and want to plot various quantities as the solver progresses. Inspired by this post, I came up with the following:

using Makie
using AbstractPlotting.MakieLayout
using AbstractPlotting
using ProgressMeter

Some fake data, in every time step, I would set the values of y[i] and y2[i]:

x = range(0, stop=1, length=1000)
y = sin.(2π*x)
y2 = cos.(2π*x)

Then, I set up the scene+layout, create some Nodes and plot them:

scene,layout = layoutscene(resolution=(800,600))

axs = layout[1:2,1] = [LAxis(scene) for i=1:2]
ii = Node(1) # Current time step
xplot = lift(i -> view(x, 1:i), ii)
yplot = lift(i -> view(y, 1:i), ii)
yplot2 = lift(i -> view(y2, 1:i), ii)

# # A single line works
# lines!(axs[1], xplot)

# This leads to some kind of race condition
lines!(axs[1], xplot, yplot)
lines!(axs[2], xplot, yplot2)

display(scene) # Show Makie window

plot_interval = 15
@showprogress for i in eachindex(x)
    if i%plot_interval == 0
        ii[] = i

        # Is this the best way to update limits?
        autolimits!.(axs)
        Makie.update!(scene)
    end
end

This leads to the following error:

ERROR: LoadError: DimensionMismatch("arrays could not be broadcast to a common size; got a dimension with lengths 30 and 15")
Stacktrace:
 [1] _bcs1 at ./broadcast.jl:501 [inlined]
 [2] _bcs at ./broadcast.jl:495 [inlined]
 [3] broadcast_shape at ./broadcast.jl:489 [inlined]
 [4] combine_axes at ./broadcast.jl:484 [inlined]
 [5] instantiate at ./broadcast.jl:266 [inlined]
 [6] materialize at ./broadcast.jl:837 [inlined]
 [7] convert_arguments(::AbstractPlotting.PointBased, ::StepRangeLen{Float64,Base.TwicePrecision{Float64},Base.TwicePrecision{Float64}}, ::SubArray{Float64,1,Array{Float64,1},Tuple{UnitRange{Int64}},true}) at /Users/jagot/.julia/packages/AbstractPlotting/lGPof/src/conversions.jl:190
 [8] convert_arguments(::Type{Lines{...}}, ::StepRangeLen{Float64,Base.TwicePrecision{Float64},Base.TwicePrecision{Float64}}, ::Vararg{Any,N} where N; kw::Base.Iterators.Pairs{Union{},Union{},Tuple{},NamedTuple{(),Tuple{}}}) at /Users/jagot/.julia/packages/AbstractPlotting/lGPof/src/conversions.jl:43
 [9] convert_arguments(::Type{Lines{...}}, ::StepRangeLen{Float64,Base.TwicePrecision{Float64},Base.TwicePrecision{Float64}}, ::SubArray{Float64,1,Array{Float64,1},Tuple{UnitRange{Int64}},true}) at /Users/jagot/.julia/packages/AbstractPlotting/lGPof/src/conversions.jl:41
 [10] (::AbstractPlotting.var"#201#203"{DataType,Observable{Tuple{Array{Point{2,Float32},1}}}})(::Tuple{}, ::Tuple{StepRangeLen{Float64,Base.TwicePrecision{Float64},Base.TwicePrecision{Float64}},SubArray{Float64,1,Array{Float64,1},Tuple{UnitRange{Int64}},true}}) at /Users/jagot/.julia/packages/AbstractPlotting/lGPof/src/interfaces.jl:617
 [11] (::Observables.OnUpdate{AbstractPlotting.var"#201#203"{DataType,Observable{Tuple{Array{Point{2,Float32},1}}}},Tuple{Observable{Tuple{}},Observable{Tuple{StepRangeLen{Float64,Base.TwicePrecision{Float64},Base.TwicePrecision{Float64}},SubArray{Float64,1,Array{Float64,1},Tuple{UnitRange{Int64}},true}}}}})(::Tuple{StepRangeLen{Float64,Base.TwicePrecision{Float64},Base.TwicePrecision{Float64}},SubArray{Float64,1,Array{Float64,1},Tuple{UnitRange{Int64}},true}}) at /Users/jagot/.julia/packages/Observables/0wrF6/src/Observables.jl:218
 [12] setindex!(::Observable{Tuple{StepRangeLen{Float64,Base.TwicePrecision{Float64},Base.TwicePrecision{Float64}},SubArray{Float64,1,Array{Float64,1},Tuple{UnitRange{Int64}},true}}}, ::Tuple{StepRangeLen{Float64,Base.TwicePrecision{Float64},Base.TwicePrecision{Float64}},SubArray{Float64,1,Array{Float64,1},Tuple{UnitRange{Int64}},true}}; notify::Observables.var"#6#8") at /Users/jagot/.julia/packages/Observables/0wrF6/src/Observables.jl:130
 [13] setindex! at /Users/jagot/.julia/packages/Observables/0wrF6/src/Observables.jl:126 [inlined]
 [14] MapUpdater at /Users/jagot/.julia/packages/Observables/0wrF6/src/Observables.jl:241 [inlined]
 [15] (::Observables.OnUpdate{Observables.MapUpdater{typeof(tuple),Tuple{StepRangeLen{Float64,Base.TwicePrecision{Float64},Base.TwicePrecision{Float64}},SubArray{Float64,1,Array{Float64,1},Tuple{UnitRange{Int64}},true}}},Tuple{Observable{StepRangeLen{Float64,Base.TwicePrecision{Float64},Base.TwicePrecision{Float64}}},Observable{SubArray{Float64,1,Array{Float64,1},Tuple{UnitRange{Int64}},true}}}})(::StepRangeLen{Float64,Base.TwicePrecision{Float64},Base.TwicePrecision{Float64}}) at /Users/jagot/.julia/packages/Observables/0wrF6/src/Observables.jl:218
 [16] setindex!(::Observable{StepRangeLen{Float64,Base.TwicePrecision{Float64},Base.TwicePrecision{Float64}}}, ::StepRangeLen{Float64,Base.TwicePrecision{Float64},Base.TwicePrecision{Float64}}; notify::Observables.var"#6#8") at /Users/jagot/.julia/packages/Observables/0wrF6/src/Observables.jl:130
 [17] setindex!(::Observable{StepRangeLen{Float64,Base.TwicePrecision{Float64},Base.TwicePrecision{Float64}}}, ::StepRangeLen{Float64,Base.TwicePrecision{Float64},Base.TwicePrecision{Float64}}) at /Users/jagot/.julia/packages/Observables/0wrF6/src/Observables.jl:126
 [18] (::Observables.MapUpdater{var"#51#52",StepRangeLen{Float64,Base.TwicePrecision{Float64},Base.TwicePrecision{Float64}}})(::Int64) at /Users/jagot/.julia/packages/Observables/0wrF6/src/Observables.jl:241
 [19] (::Observables.OnUpdate{Observables.MapUpdater{var"#51#52",StepRangeLen{Float64,Base.TwicePrecision{Float64},Base.TwicePrecision{Float64}}},Tuple{Observable{Int64}}})(::Int64) at /Users/jagot/.julia/packages/Observables/0wrF6/src/Observables.jl:218
 [20] setindex!(::Observable{Int64}, ::Int64; notify::Observables.var"#6#8") at /Users/jagot/.julia/packages/Observables/0wrF6/src/Observables.jl:130
 [21] setindex!(::Observable{Int64}, ::Int64) at /Users/jagot/.julia/packages/Observables/0wrF6/src/Observables.jl:126

which I interpret as somehow, it tries to update the plot, before both xplot and yplot have been updated.

How can I solve this?

Because of the synchronous update of the Observables, this usually happens. The way around it is to pass an observable vector of points, in your case:

points1 = lift(i->Point2f0.(view(x, 1:i), view(y, 1:i)), ii)
points2 = lift(i->Point2f0.(view(x, 1:i), view(y2, 1:i)), ii)
lines!(axs[1], points1)
lines!(axs[2], points2)

This line is unnecessary:

Makie.update!(scene)

This works very nicely, thank you!