Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implemented async Tracker with explicit values. #12294

Merged
merged 3 commits into from Jan 6, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
26 changes: 14 additions & 12 deletions packages/tracker/tracker.js
Expand Up @@ -32,11 +32,6 @@ Tracker.active = false;
*/
Tracker.currentComputation = null;

function setCurrentComputation(c) {
Tracker.currentComputation = c;
Tracker.active = !! c;
}

function _debugFunc() {
// We want this code to work without Meteor, and also without
// "console" (which is technically non-standard and may be missing
Expand Down Expand Up @@ -300,14 +295,13 @@ Tracker.Computation = class Computation {
_compute() {
this.invalidated = false;

var previous = Tracker.currentComputation;
setCurrentComputation(this);
var previousInCompute = inCompute;
inCompute = true;
try {
withNoYieldsAllowed(this._func)(this);
Tracker.withComputation(this, () => {
withNoYieldsAllowed(this._func)(this);
});
} finally {
setCurrentComputation(previous);
inCompute = previousInCompute;
}
}
Expand Down Expand Up @@ -597,12 +591,20 @@ Tracker.autorun = function (f, options) {
* @param {Function} func A function to call immediately.
*/
Tracker.nonreactive = function (f) {
var previous = Tracker.currentComputation;
setCurrentComputation(null);
return Tracker.withComputation(null, f);
};

Tracker.withComputation = function (computation, f) {
var previousComputation = Tracker.currentComputation;

Tracker.currentComputation = computation;
Tracker.active = !!computation;

try {
return f();
} finally {
setCurrentComputation(previous);
Tracker.currentComputation = previousComputation;
Tracker.active = !!previousComputation;
}
};

Expand Down
112 changes: 112 additions & 0 deletions packages/tracker/tracker_tests.js
Expand Up @@ -518,6 +518,118 @@ testAsyncMulti('tracker - Tracker.autorun, onError option', [function (test, exp
Tracker.flush();
}]);

Tinytest.addAsync('tracker - async function - basics', function (test, onComplete) {
const computation = Tracker.autorun(async function (computation) {
test.equal(computation.firstRun, true, 'before (firstRun)');
test.equal(Tracker.currentComputation, computation, 'before');
const x = await Promise.resolve().then(() =>
Tracker.withComputation(computation, () => {
// The `firstRun` is `false` as soon as the first `await` happens.
test.equal(computation.firstRun, false, 'inside (firstRun)');
test.equal(Tracker.currentComputation, computation, 'inside');
return 123;
})
);
test.equal(x, 123, 'await (value)');
test.equal(computation.firstRun, false, 'await (firstRun)');
Tracker.withComputation(computation, () => {
test.equal(Tracker.currentComputation, computation, 'await');
});
await new Promise(resolve => setTimeout(resolve, 10));
Tracker.withComputation(computation, () => {
test.equal(computation.firstRun, false, 'sleep (firstRun)');
test.equal(Tracker.currentComputation, computation, 'sleep');
});
try {
await Promise.reject('example');
test.fail();
} catch (error) {
Tracker.withComputation(computation, () => {
test.equal(error, 'example', 'catch (error)');
test.equal(computation.firstRun, false, 'catch (firstRun)');
test.equal(Tracker.currentComputation, computation, 'catch');
});
}
onComplete();
});

test.equal(Tracker.currentComputation, null, 'outside (computation)');
test.instanceOf(computation, Tracker.Computation, 'outside (result)');
});

Tinytest.addAsync('tracker - async function - interleaved', async function (test) {
let count = 0;
const limit = 100;
for (let index = 0; index < limit; ++index) {
Tracker.autorun(async function (computation) {
test.equal(Tracker.currentComputation, computation, `before (${index})`);
await new Promise(resolve => setTimeout(resolve, Math.random() * limit));
count++;
Tracker.withComputation(computation, () => {
test.equal(Tracker.currentComputation, computation, `after (${index})`);
});
});
}

test.equal(count, 0, 'before resolve');
await new Promise(resolve => setTimeout(resolve, limit));
test.equal(count, limit, 'after resolve');
});

Tinytest.addAsync('tracker - async function - parallel', async function (test) {
let resolvePromise;
const promise = new Promise(resolve => {
resolvePromise = resolve;
});

let count = 0;
const limit = 100;
const dependency = new Tracker.Dependency();
for (let index = 0; index < limit; ++index) {
Tracker.autorun(async function (computation) {
count++;
Tracker.withComputation(computation, () => {
dependency.depend();
});
await promise;
count--;
});
}

test.equal(count, limit, 'before');
dependency.changed();
await new Promise(setTimeout);
test.equal(count, limit * 2, 'changed');
resolvePromise();
await new Promise(setTimeout);
test.equal(count, 0, 'after');
});

Tinytest.addAsync('tracker - async function - stepped', async function (test) {
let resolvePromise;
const promise = new Promise(resolve => {
resolvePromise = resolve;
});

let count = 0;
const limit = 100;
for (let index = 0; index < limit; ++index) {
Tracker.autorun(async function (computation) {
test.equal(Tracker.currentComputation, computation, `before (${index})`);
await promise;
count++;
Tracker.withComputation(computation, () => {
test.equal(Tracker.currentComputation, computation, `after (${index})`);
});
});
}

test.equal(count, 0, 'before resolve');
resolvePromise();
await new Promise(setTimeout);
test.equal(count, limit, 'after resolve');
});

Tinytest.add('computation - #flush', function (test) {
var i = 0, j = 0, d = new Tracker.Dependency;
var c1 = Tracker.autorun(function () {
Expand Down