computed-async-mobx
Version:
Define a computed by returning a Promise
129 lines (79 loc) • 3.4 kB
text/typescript
import test from "blue-tape";
import { testStrictness, waitForLength, Obs } from "./util";
import { delay } from "./delay";
import { observable, runInAction, autorun } from "mobx"
import { throttledComputed } from "../src/index"
function getInsideReaction<T>(getter: () => T) {
let result: T = undefined!;
const stop = autorun(() => {
result = getter();
});
stop();
return result;
}
testStrictness("throttledComputed - synchronous at first", async (assert: test.Test) => {
const o = observable({ x: 1, y: 2 });
const r = throttledComputed(() => o.x + o.y, 50);
assert.equal(getInsideReaction(() => r.get()), 3, "Initial computation is synchronous");
runInAction(() => o.x = 5);
assert.equal(getInsideReaction(() => r.get()), 7, "Subsequent computations outside reactive contexts are also synchronous");
runInAction(() => o.x = 6);
assert.equal(getInsideReaction(() => r.get()), 8, "Ditto");
const results: number[] = [];
const stop = autorun(() => results.push(r.get()));
assert.deepEqual(results, [8]);
runInAction(() => o.x = 3);
assert.deepEqual(results, [8], "Reactive contexts don't see immediate changes");
await waitForLength(results, 2);
assert.deepEqual(results, [8, 5], "But do see delayed changes");
runInAction(() => o.x = 10);
runInAction(() => o.x = 20);
await waitForLength(results, 3);
assert.deepEqual(results, [8, 5, 22], "Changes are batched by throttling");
stop();
});
testStrictness("throttledComputed - propagates exceptions", async (assert: test.Test) => {
const o = new Obs(false);
const r = throttledComputed(() => {
if (o.get()) {
throw new Error("Badness");
}
return 1;
}, 50);
assert.equal(getInsideReaction(() => r.get()), 1, "Initial computation is synchronous");
const results: (number | string)[] = [];
const stop = autorun(() => {
try {
results.push(r.get());
} catch(x) {
results.push(x.message);
}
});
assert.deepEqual(results, [1]);
runInAction(() => o.set(true));
assert.deepEqual(results, [1], "Reactive contexts don't see immediate changes");
await waitForLength(results, 2);
assert.deepEqual(results, [1, "Badness"], "But do see delayed changes");
runInAction(() => o.set(false));
runInAction(() => o.set(true));
runInAction(() => o.set(false));
await waitForLength(results, 3);
assert.deepEqual(results, [1, "Badness", 1], "Changes are batched by throttling");
runInAction(() => o.set(true));
await delay(1);
runInAction(() => o.set(false));
runInAction(() => o.set(true));
await waitForLength(results, 4);
assert.deepEqual(results, [1, "Badness", 1, "Badness"], "Changes are batched again");
stop();
});
testStrictness("throttledComputed - can be refreshed", async (assert: test.Test) => {
let counter = 0;
const r = throttledComputed(() => ++counter, 10);
const trace: (number)[] = [];
const stop = autorun(() => trace.push(r.get()));
assert.deepEqual(trace, [1], "Initial value appears synchronously");
r.refresh();
assert.deepEqual(trace, [1, 2], "Second value appears synchronously");
stop();
});