diff --git a/docs/api/reactivity-utility.md b/docs/api/reactivity-utility.md index a16e251..ee82067 100644 --- a/docs/api/reactivity-utility.md +++ b/docs/api/reactivity-utility.md @@ -73,4 +73,22 @@ read can still be tracked inside a reactive scope. function read(value: T | () -> T): T ``` +## batch() + +Runs a given function where any source updates made within the function do not +trigger effects until after the function runs. + +- **Type** + + ```lua + function batch(fn: () -> ()) + ``` + +- **Details** + + Improves performance when an effect depends on multiple sources, and those + sources need to be updated. Updating those sources inside a batch call will + only cause the effect to run once after the batch call ends instead of after + each time a source is updated. + -------------------------------------------------------------------------------- diff --git a/src/batch.luau b/src/batch.luau index 3c08127..1951789 100644 --- a/src/batch.luau +++ b/src/batch.luau @@ -11,13 +11,15 @@ local function batch(setter: () -> ()) local ok, err: string? = pcall(setter) - flags.batch = false - - if not ok then throw(`error occured while batching updates: {err}`) end - - if not already_batching then -- todo: flush anyways? - graph.flush_update_queue() + if not already_batching then + flags.batch = false + + if not already_batching then + graph.flush_update_queue() + end end + + if not ok then throw(`error occured while batching updates: {err}`) end end return batch diff --git a/src/graph.luau b/src/graph.luau index 9288a82..66587a8 100644 --- a/src/graph.luau +++ b/src/graph.luau @@ -185,8 +185,11 @@ local function queue_children(node: StartNode) update_queue.n = i end +local _flushing = false local function flush_update_queue() - -- todo: test with recursive batch sets + assert(not flushing, "recursive queue flush occured") -- todo + _flushing = true + local n0 = 0 local i = n0 + 1 @@ -203,6 +206,8 @@ local function flush_update_queue() end update_queue.n = n0 + + _flushing = false end local function update(root: StartNode) diff --git a/test/tests.luau b/test/tests.luau index 9cd6afe..94c7bb4 100644 --- a/test/tests.luau +++ b/test/tests.luau @@ -1805,7 +1805,7 @@ TEST("batch()", wrap_root(function() local derive = vide.derive local batch = vide.batch - do CASE "child evaluation halted" + do CASE "evaluation deferred" local a = source(0) local count = { b = 0, b2 = 0, c = 0 } @@ -1840,7 +1840,47 @@ TEST("batch()", wrap_root(function() CHECK(c() == 5) end - -- todo: test batch call in recursive set + do CASE "recursive call" + local a1 = source(0) + local a2 = source(0) + local a3 = source(0) + + local count = { b1 = 0, b2 = 0, b3 = 0 } + + local b1 = derive(function() + count.b1 += 1 + return a1() + 1 + end) + + local b2 = derive(function() + count.b2 += 1 + return a2() + 1 + end) + + local b3 = derive(function() + count.b3 += 1 + return a3() + 1 + end) + + batch(function() + a1(1) + batch(function() + a2(2) + end) + a3(3) + CHECK(count.b1 == 1) + CHECK(count.b2 == 1) + CHECK(count.b3 == 1) + end) + + CHECK(count.b1 == 2) + CHECK(count.b2 == 2) + CHECK(count.b3 == 2) + + CHECK(b1() == 2) + CHECK(b2() == 3) + CHECK(b3() == 4) + end end)) TEST("read()", wrap_root(function() diff --git a/todo.md b/todo.md index 1256cdd..6f254bd 100644 --- a/todo.md +++ b/todo.md @@ -1,10 +1,4 @@ # todo -- optimize wide graph updating -- implement from solid: - - stores - - portals - - batch() -- optimize `indexes()` double-diffing -- cleanup source and tests +- improve error traces - prevent redundant re-eval of nodes in a complex diamond graph