-
Notifications
You must be signed in to change notification settings - Fork 73
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
Async Runner with Message Channel #458
base: main
Are you sure you want to change the base?
Async Runner with Message Channel #458
Conversation
|
||
export const ASYNC_TEST_INVOKER_LOOKUP = { | ||
__proto__: null, | ||
timer: AsyncTimerTestInvoker, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we don't want an AsyncTimerTestInvoker, I could simply assign the same AsyncRAFTestInvoker..
class AsyncRAFTestInvoker extends BaseRAFTestInvoker { | ||
_scheduleCallbacks(resolve) { | ||
requestAnimationFrame(async () => { | ||
await this._syncCallback(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
_syncCallback and _asyncCallback should be renamed to something else, since, well, the patch makes _syncCallback rather asynchronous :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LOL, we had the same convo in a similar pr... what do you think about:
class TestInvoker {
constructor(captureTest, captureLayout, reportResults) {
this._captureTest = captureTest;
this._captureLayout = captureLayout;
this._reportResults = reportResults;
}
}
tryTriggerAsyncCallback(); | ||
}); | ||
|
||
const mc = new MessageChannel(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It might be nice to use the same channel for all the tests, just to avoid extra garbage created by the runner. Not sure where there channel could live. Perhaps a static property on the class?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
are you thinking maybe something like:
class AsyncRAFTestInvoker extends BaseRAFTestInvoker {
static mc = new MessageChannel();
_scheduleCallbacks(resolve) {
requestAnimationFrame(async () => {
await this._syncCallback();
let gotTimer = false;
let gotMessage = false;
const tryTriggerAsyncCallback = () => {
if (!gotTimer || !gotMessage)
return;
this._asyncCallback();
setTimeout(async () => {
await this._reportCallback();
resolve();
}, 0);
};
const handleMessage = () => {
AsyncRAFTestInvoker.mc.port1.removeEventListener("message", handleMessage);
gotMessage = true;
tryTriggerAsyncCallback();
};
setTimeout(() => {
gotTimer = true;
tryTriggerAsyncCallback();
});
AsyncRAFTestInvoker.mc.port1.addEventListener("message", handleMessage);
AsyncRAFTestInvoker.mc.port1.start();
AsyncRAFTestInvoker.mc.port2.postMessage("speedometer");
});
}
}
setTimeout(() => { | ||
gotTimer = true; | ||
tryTriggerAsyncCallback(); | ||
}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
oh, hmm, does this non-double rAF setup have a bug after all. If the promise callbacks create new promises which get resolve and those callbacks then trigger setTimeout or postMessage, does anything guarantee that test runners setTimeout/messageport gets handled after those?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We are stopping and waiting for await this._syncCallback();
.
The setTimeout / messageport happens after that resolved, correct?
The reason of having the boolean flags is that we can't guarantee in which order setTimeout / postMessage gets called. At least that's how I understood our conversation 🙂
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I did test it with the current tests as well as with a test that had an artificial promise resolving after a long period of time in each step.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But await this._syncCallback(); might return in the same task, if one just does effectively
return new Promise(r => r());
In my example using MessageChannel there were still two rAFs.
I thought we were thinking to use the same runner for async tests and non-async, and I'm worried that not having two rafs would broke non-async tests (meaning that some time wouldn't be measured correctly). But I could be missing something (I think I did when I said on Slack couple of days ago that this approach is ok :)).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if we don't want nested rafs, this might be a way maybe:
_scheduleCallbacks(resolve) {
let gotTimer = false;
let gotMessage = false;
let gotPromise = false;
const tryTriggerAsyncCallback = () => {
if (!gotTimer || !gotMessage || !gotPromise)
return;
this._asyncCallback();
setTimeout(async () => {
await this._reportCallback();
resolve();
}, 0);
};
requestAnimationFrame(async () => {
await this._syncCallback();
gotPromise = true;
tryTriggerAsyncCallback();
});
requestAnimationFrame(() => {
setTimeout(() => {
gotTimer = true;
tryTriggerAsyncCallback();
});
const mc = new MessageChannel();
mc.port1.onmessage = () => {
mc.port1.close();
mc.port2.close();
gotMessage = true;
tryTriggerAsyncCallback();
};
mc.port2.postMessage("speedometer");
});
}
As discussed in our sync, here is a combination of the AsyncRunner and Message Channel pr.
Relevant code snippet:
For testing purposes I kept an explicit type of "async" in the test file, but I opted all default suites into using it.
For now, I still kept the AsyncTimerClass, which can get removed once we settle on the code snippet above.
Initial testing shows that it should also fix the react specific issue.