Skip to content

Commit ebc7149

Browse files
authored
Generalise the notifying semantics to freezing (#223)
* Rename `notifying` to `frozen` This reflects an upcoming change which broadens its scope. * Freeze out both `watch` and `unwatch` * Separate out the steps of this algorithm * Freeze the graph during execution of the `watched` callback * Spell out the `unwatch` algorithm in steps * Freeze the graph during execution of the `unwatched` callback * Correct nesting
1 parent e84c250 commit ebc7149

File tree

1 file changed

+22
-12
lines changed

1 file changed

+22
-12
lines changed

README.md

+22-12
Original file line numberDiff line numberDiff line change
@@ -443,7 +443,7 @@ Some aspects of the algorithm:
443443
Signal algorithms need to reference certain global state. This state is global for the entire thread, or "agent".
444444
445445
- `computing`: The innermost computed or effect Signal currently being reevaluated due to a `.get` or `.run` call, or `undefined`. Initially `undefined`.
446-
- `notifying`: Boolean denoting whether there is an `notify` callback currently executing. Initially `false`.
446+
- `frozen`: Boolean denoting whether there is a callback currently executing which requires that the graph not be modified. Initially `false`.
447447
- `generation`: An incrementing integer, starting at 0, used to track how current a value is while avoiding circularities.
448448
449449
### The `Signal` namespace
@@ -473,22 +473,22 @@ Signal algorithms need to reference certain global state. This state is global f
473473
474474
#### Method: `Signal.State.prototype.get()`
475475
476-
1. If `notifying` is true, throw an exception.
476+
1. If `frozen` is true, throw an exception.
477477
1. If `computing` is not `undefined`, add this Signal to `computing`'s `sources` set.
478478
1. NOTE: We do not add `computing` to this Signal's `sinks` set until it is watched by a Watcher.
479479
1. Return this Signal's `value`.
480480
481481
#### Method: `Signal.State.prototype.set(newValue)`
482482
483-
1. If the current execution context is `notifying`, throw an exception.
483+
1. If the current execution context is `frozen`, throw an exception.
484484
1. Run the "set Signal value" algorithm with this Signal and the first parameter for the value.
485485
1. If that algorithm returned `~clean~`, then return undefined.
486486
1. Set the `state` of all `sinks` of this Signal to (if it is a Computed Signal) `~dirty~` if they were previously clean, or (if it is a Watcher) `~pending~` if it was previously `~watching~`.
487487
1. Set the `state` of all of the sinks' Computed Signal dependencies (recursively) to `~checked~` if they were previously `~clean~` (that is, leave dirty markings in place), or for Watchers, `~pending~` if previously `~watching~`.
488488
1. For each previously `~watching~` Watcher encountered in that recursive search, then in depth-first order,
489-
1. Set `notifying` to true.
489+
1. Set `frozen` to true.
490490
1. Calling their `notify` callback (saving aside any exception thrown, but ignoring the return value of `notify`).
491-
1. Restore `notifying` to false.
491+
1. Restore `frozen` to false.
492492
1. Set the `state` of the Watcher to `~waiting~`.
493493
1. If any exception was thrown from the `notify` callbacks, propagate it to the caller after all `notify` callbacks have run. If there are multiple exceptions, then package them up together into an AggregateError and throw that.
494494
1. Return undefined.
@@ -548,7 +548,7 @@ With [AsyncContext](https://github.com/tc39/proposal-async-context), the callbac
548548
549549
#### Method: `Signal.Computed.prototype.get`
550550
551-
1. If the current execution context is `notifying` or if this Signal has the state `~computing~`, or if this signal is an Effect and `computing` a computed Signal, throw an exception.
551+
1. If the current execution context is `frozen` or if this Signal has the state `~computing~`, or if this signal is an Effect and `computing` a computed Signal, throw an exception.
552552
1. If `computing` is not `undefined`, add this Signal to `computing`'s `sources` set.
553553
1. NOTE: We do not add `computing` to this Signal's `sinks` set until/unless it becomes watched by a Watcher.
554554
1. If this Signal's state is `~dirty~` or `~checked~`: Repeat the following steps until this Signal is `~clean~`:
@@ -601,18 +601,28 @@ With [AsyncContext](https://github.com/tc39/proposal-async-context), the callbac
601601
602602
#### Method: `Signal.subtle.Watcher.prototype.watch(...signals)`
603603
604+
1. If `frozen` is true, throw an exception.
604605
1. If any of the arguments is not a signal, throw an exception.
605606
1. Append all arguments to the end of this object's `signals`.
606-
1. Add this watcher to each of the newly watched signals as a sink.
607-
1. Add this watcher as a `sink` to each Signal. If this was the first sink, then recurse up to sources to add that signal as a sink, and call the `watched` callback if it exists.
607+
1. For each newly-watched signal, in left-to-right order,
608+
1. Add this watcher as a `sink` to that signal.
609+
1. If this was the first sink, then recurse up to sources to add that signal as a sink.
610+
1. Set `frozen` to true.
611+
1. Call the `watched` callback if it exists.
612+
1. Restore `frozen` to true.
608613
1. If the Signal's `state` is `~waiting~`, then set it to `~watching~`.
609614
610615
#### Method: `Signal.subtle.Watcher.prototype.unwatch(...signals)`
611616
617+
1. If `frozen` is true, throw an exception.
612618
1. If any of the arguments is not a signal, or is not being watched by this watcher, throw an exception.
613-
1. Remove each element from signals from this object's `signals`.
614-
1. Remove this Watcher from that Signal's `sink` set.
615-
1. If any Signal's `sink` set is now empty, then remove itself as a sink from each of its sources, and call the `unwatched` callback if it exists
619+
1. For each signal in the arguments, in left-to-right order,
620+
1. Remove that signal from this Watcher's `signals` set.
621+
1. Remove this Watcher from that Signal's `sink` set.
622+
1. If that Signal's `sink` set has become empty, remove that Signal as a sink from each of its sources.
623+
1. Set `frozen` to true.
624+
1. Call the `unwatched` callback if it exists.
625+
1. Restore `frozen` to false.
616626
1. If the watcher now has no `signals`, and its `state` is `~watching~`, then set it to `~waiting~`.
617627
618628
#### Method: `Signal.subtle.Watcher.prototype.getPending()`
@@ -627,7 +637,7 @@ With [AsyncContext](https://github.com/tc39/proposal-async-context), the callbac
627637
1. Restore `computing` to `c` (even if `cb` threw an exception).
628638
1. Return the return value of `cb` (rethrowing any exception).
629639
630-
Note: untrack doesn't get you out of the `notifying` state, which is maintained strictly.
640+
Note: untrack doesn't get you out of the `frozen` state, which is maintained strictly.
631641
632642
### Common algorithms
633643

0 commit comments

Comments
 (0)