Skip to content

Commit

Permalink
next() and until() should not resume inside rule
Browse files Browse the repository at this point in the history
  • Loading branch information
pjeby committed May 27, 2024
1 parent c8cf703 commit bf4667b
Show file tree
Hide file tree
Showing 3 changed files with 21 additions and 20 deletions.
20 changes: 10 additions & 10 deletions specs/jobs.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -676,7 +676,7 @@ describe("Async Ops", () => {
// Then it should remain suspended
see();
// Until the value *changes*, even if false
v.set(0); runRules(); see("0")
v.set(0); runRules(); clock.tick(0); see("0")

});
it("asynchronously resuming when signal changes", () => {
Expand All @@ -685,23 +685,23 @@ describe("Async Ops", () => {
suspendOn(next(v)); clock.runAll(); runRules(); see();
// When the changes and rules run
v.set(55); see(); runRules();
// Then the next should resume with the new value
see("55");
// Then the next should resume asynchronously with the new value
clock.tick(0); see("55");
});
it("throwing when a signal throws synchronously", () => {
// When a suspended next() is run on an immediately throwing signal
suspendOn(next(cached(() => {throw "boom"}))); clock.runAll(); runRules()
// Then it should immediately throw
see("err: boom");
// Then it should asynchronously throw
see(); clock.tick(0); see("err: boom");
});
it("asynchronously throwing when a signal throws later", () => {
// Given an async-throwing signal and an next() suspended on it
const v = value(20), c = cached(() => { if (v()) throw "boom!";});
suspendOn(next(c)); clock.runAll(); see();
// When the signal recomputes as an error
v.set(55); see(); runRules();
// Then the next should reject with the error
see("err: boom!");
v.set(55); see(); runRules(); see();
// Then the next should asynchronously reject with the error
clock.tick(0); see("err: boom!");
});
});
it("throws on non-Waitables", () => {
Expand Down Expand Up @@ -756,7 +756,7 @@ describe("Async Ops", () => {
const v = value(0);
suspendOn(until(v)); clock.runAll(); see();
// When the value becomes true and rules run
v.set(55); see(); runRules();
v.set(55); see(); runRules(); clock.tick(0);
// Then the until should resume with the new value
see("55");
});
Expand All @@ -771,7 +771,7 @@ describe("Async Ops", () => {
const v = value(0), c = cached(() => { if (v()) throw "boom!";});
suspendOn(until(c)); clock.runAll(); see();
// When the signal recomputes as an error
v.set(55); see(); runRules();
v.set(55); see(); runRules(); clock.tick(0)
// Then the until should reject with the error
see("err: boom!");
});
Expand Down
12 changes: 6 additions & 6 deletions specs/signals.spec.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { log, see, describe, expect, it, useRoot, useClock, clock } from "./dev_deps.ts";
import {
runRules, value, cached, rule, peek, WriteConflict, Signal, Writable, must, recalcWhen,
DisposeFn, RecalcSource, mockSource, lazy, detached, each, sleep, isCancel, getJob,
DisposeFn, RecalcSource, mockSource, lazy, detached, each, sleep,
SignalImpl, ConfigurableImpl, action
} from "../mod.ts";
import { current } from "../src/ambient.ts";
Expand Down Expand Up @@ -110,16 +110,16 @@ describe("Signal Constructors/Interfaces", () => {
// Then the subscriber should be run in the null context
see("true"); c.end();
});
it("cleans up its until() rules", () => {
it("doesn't resume until() inside a rule", () => {
// Given a falsy value
const v = value(false);
// When it's waited for via until and then goes truthy
for(const cb of v["uneventful.until"]()) {
cb(() => { log(isCancel(getJob().result())); });
cb(() => { log(!!current.cell); });
}
v.set(true); runRules();
// Then the resolve should be in a canceled job (rule)
see("true");
v.set(true); runRules(); see();
// Then the resolve should occur asynchronously without being in a rule
clock.tick(0); see("false");
});
});
describe(".setf()", () => {
Expand Down
9 changes: 5 additions & 4 deletions src/signals.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { reject, resolve } from "./results.ts";
import { UntilMethod } from "./sinks.ts";
import { SignalSource, Source } from "./streams.ts";
import { CallableObject } from "./utils.ts";
import { defer } from "./defer.ts";

export { rule, runRules, type GenericMethodDecorator, type RuleFactory } from "./rules.ts"
export { WriteConflict, CircularDependency } from "./cells.ts";
Expand Down Expand Up @@ -82,8 +83,8 @@ export class SignalImpl<T> extends CallableObject<SignalSource<T>> implements Si
return yield (r => {
let seen = false, res: T;
rule(stop => {
try { res = this(); } catch(e) { stop(); reject(r, e); }
if (seen) { stop(); resolve(r, res); }
try { res = this(); } catch(e) { stop(); defer(reject.bind(null, r,e)); }
if (seen) { stop(); defer(resolve.bind(null, r, res)); }
seen = true;
})
});
Expand All @@ -95,8 +96,8 @@ export class SignalImpl<T> extends CallableObject<SignalSource<T>> implements Si
try { res = this(); } catch(e) { reject(r, e); return; }
if (res) return resolve(r, res);
rule(stop => {
try { res = this(); } catch(e) { stop(); reject(r,e); }
if (res) { stop(); resolve(r, res); }
try { res = this(); } catch(e) { stop(); defer(reject.bind(null, r,e)); }
if (res) { stop(); defer(resolve.bind(null, r, res)); }
});
});
}
Expand Down

0 comments on commit bf4667b

Please sign in to comment.