tc39 / proposal-signals

A proposal to add signals to JavaScript.
MIT License
2.87k stars 54 forks source link

Exceptions in `watched` callback leave `Watcher` in inconsistent state #209

Open prophile opened 2 weeks ago

prophile commented 2 weeks ago

Consider the following code:

const a = new Signal.State(0)
const b = new Signal.State(1, {
  [Signal.subtle.watched]: () => {
    throw new Error("this failed");
  },
})
const c = new Signal.State(2)

const watcher = new Signal.subtle.Watcher(() => {
  console.log("notified")
});
try {
  watcher.watch(a, b, c);
} catch (e) {
  console.log(e.message)
}

console.log("a sinks", Signal.subtle.introspectSinks(a))
console.log("b sinks", Signal.subtle.introspectSinks(b))
console.log("c sinks", Signal.subtle.introspectSinks(c))
console.log("watcher sources", Signal.subtle.introspectSources(watcher))

In the polyfill at f7c550b, and according to the algorithm in "Method: Signal.subtle.Watcher.prototype.watch(...signals)", this error thrown in the watched callback leaves Watcher in an inconsistent state. a has the watcher as a sink but neither b nor c do (so we have a case where watcher.watch has added some, but not all, of its arguments as sources); also, the watcher lists both a and b as sources so its state is inconsistent as to whether b is watched or not.