Skip to content

Commit

Permalink
adding new test and committing old code
Browse files Browse the repository at this point in the history
  • Loading branch information
mmocny committed Mar 7, 2024
1 parent 594bf86 commit 9fbb5ba
Show file tree
Hide file tree
Showing 10 changed files with 233 additions and 21 deletions.
4 changes: 2 additions & 2 deletions fps-meter/bak/main.mjs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { FpsTracker } from '../FpsTracker.mjs';
import { CanvasFps } from '../CanvasFps.mjs';
import { FpsTracker } from '../FpsTracker.js';
import { CanvasFps } from './CanvasFps.js';
import { ThreadLocalRAFIterator, SendPostMessageRAF } from '../AnimationFrameIterator.js';


Expand Down
Empty file added fps-meter/fps-meter.js
Empty file.
13 changes: 0 additions & 13 deletions lib/raf.js

This file was deleted.

64 changes: 64 additions & 0 deletions lib/scheduling-utils.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
export async function raf(signal) {
return new Promise((resolve, reject) => {
// TODO: Alternative: if signal.aborted reject(signal.reason)
signal?.throwIfAborted();

// TODO: is it needed to remove abort eventlistener after successful resolve?
const rafid = requestAnimationFrame(resolve);

signal?.addEventListener('abort', (ev) => {
cancelAnimationFrame(rafid);
// TODO: Is this useful?
reject(signal.reason);
}, { once: true });
});
}

export async function afterNextPaint() {
// TODO: experiment with scheduler.render() when available
await raf();
// TODO: polyfill as needed
await scheduler.yield();
}

export async function delay(ms) {
return new Promise(resolve => {
setTimeout(resolve, ms);
});
}

export function block(ms) {
const taget = performance.now() + ms;
while (performance.now() < taget);
}

export async function waitForEvent(target, eventName) {
return new Promise(resolve => {
target.addEventListener(eventName, resolve, { once: true });
});
}

export async function idleUntilBeforeRender(opts = {}) {
// Default to highest priority, but you may want to use background
opts = {
priority: 'user-blocking',
...opts,
};
return new Promise(resolve => {
const contoller = new AbortController();

const rafid = requestAnimationFrame(() => {
contoller.abort();
resolve();
});

const taskid = scheduler.postTask(() => {
cancelAnimationFrame(rafid);
resolve();
}, {
priority: 'user-blocking',
signal: contoller.signal,
});
});
}

2 changes: 1 addition & 1 deletion live-expressions/lib/once-per-pageload.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import sha256 from "./str-to-hash";
import sha256 from "../../lib/str-to-hash";

// This is mostly useful for DevTools live expressions
export default function oncePerPageload(callback) {
Expand Down
2 changes: 1 addition & 1 deletion live-expressions/web-mightals.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { webMightals$ } from '../web-mightals.js';
import { webMightals$ } from '../sandbox/web-mightals.js/index.js';
import oncePerPageload from "./lib/once-per-pageload.js";

function main() {
Expand Down
12 changes: 8 additions & 4 deletions sandbox/react-flushSync/hooks/useAbortSignallingTransition.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,22 @@ import useAwaitableTransition from "./useAwaitableTransition";

export default function useAbortSignallingTransition() {
const [isPending, startAwaitableTransition] = useAwaitableTransition();
const abortController = useRef();
const abortController = useRef(new AbortController);

const startAbortSignallingTransition = useCallback(async (callback) => {
abortController.current = new AbortController;
let prevAC = abortController.current;
let nextAC = new AbortController();
abortController.current = nextAC;

try {
await startAwaitableTransition(callback);
} catch(ex) {
abortController.current.abort();
// abortController.current has already been updated by the time we catch,
// so use the previously saved `prevAC` value
prevAC.abort();
// throw ex;
}
}, [startAwaitableTransition]);

return [isPending, startAbortSignallingTransition, abortController.signal];
return [isPending, startAbortSignallingTransition, abortController.current.signal];
};
157 changes: 157 additions & 0 deletions sandbox/soft-nav-test-dom-edit-append-tree/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@

<body>
<button id="dom_mod">Dom Mod</button>
<button id="inner_html">Inner HTML</button>
<button id="clone_template">Clone Template</button>
<button id="detached_doc">Detached Doc</button>
<main id="main">Content</main>
</body>

<script type="module">

function update_dom_mod(target) {
const content = document.createElement('div');
target.replaceChildren(content);
content.textContent = 'Hi ';
const table = document.createElement('table');
content.appendChild(table);
const tr = document.createElement('tr');
table.appendChild(tr);
const td = document.createElement('td');
tr.appendChild(td);
td.textContent = 'DOM MOD';
}

function update_inner_html(target) {
target.innerHTML = '<div>Hi <table><tr><td>INNER HTML</td></tr></table></div>';
}

function update_clone_template(target) {
const template = document.createElement('template');
template.innerHTML = '<div>Hi <table><tr><td>CLONE TEMPLATE</td></tr></table></div>';
const content = template.content.cloneNode(true);
target.replaceChildren(content);
}

function update_detached_doc(target) {
const doc = document.implementation.createHTMLDocument('');
doc.open();
doc.write('<div>Hi <table><tr><td>DETACHED DOC</td></tr></table></div>');
const content = doc.body.firstChild;
target.replaceChildren(content);
}

dom_mod.addEventListener('click', event => update_dom_mod(main));
inner_html.addEventListener('click', event => update_inner_html(main));
clone_template.addEventListener('click', event => update_clone_template(main));
detached_doc.addEventListener('click', event => update_detached_doc(main));

document.addEventListener('click', event => {
setTimeout(() => {
history.pushState(null, '', event.target.id);
}, 1000);
})

</script>


<script type="module">
const RATING_COLORS = {
"good": "#0CCE6A",
"needs-improvement": "#FFA400",
"poor": "#FF4E42",
"invalid": "#FFC0CB",
"default": "inherit", // Will default to this, anyway
};

function log(metric) {
const prettyScore = metric.value.toLocaleString(undefined, { maximumFractionDigits: 0 });
console.groupCollapsed(
`[${metric.name}] %c${prettyScore} ms (${
metric.rating
})`,
`color: ${RATING_COLORS[metric.rating] || "inherit"}`
);

console.log(metric);
console.log(...metric.entries);
console.groupEnd();
}

function getNavigationEntry(navigationId) {
const navs = [...performance.getEntriesByType('navigation'), ...performance.getEntriesByType('soft-navigation')];
// console.log(navs, navigationId);
return navs.filter(nav => nav.navigationId == navigationId)[0];
};

const valueToRating = (score) =>
score <= 0 ? "invalid" : score <= 2500 ? "good" : score <= 4000 ? "needs-improvement" : "poor";


// TODO: Ignore non-soft LCP.
const observer = new PerformanceObserver((entryList) => {
for (const paintEntry of entryList.getEntries()) {
const navEntry = getNavigationEntry(paintEntry.navigationId);
const name = `Soft.${(paintEntry.name || paintEntry.entryType).split('-').map((s)=>s[0].toUpperCase()).join('')}`;
const value = paintEntry.startTime - navEntry.startTime;
const metric = {
attribution: {
navEntry,
paintEntry,
pageUrl: navEntry.name,
elementUrl: paintEntry.url,
},
entries: [paintEntry],
name,
rating: valueToRating(value),
value,
};

performance.measure(name, {
start: navEntry.startTime,
end: paintEntry.startTime,
});

log(metric);

console.log(paintEntry.element, paintEntry.size);
}
});

observer.observe({
type: 'paint',
buffered: true,
includeSoftNavigationObservations: true,
});
observer.observe({
type: 'largest-contentful-paint',
buffered: true,
includeSoftNavigationObservations: true,
});

const observer2 = new PerformanceObserver((entryList) => {
for (const entry of entryList.getEntries()) {
const name = `Soft.Nav`;
const value = entry.duration;
const metric = {
attribution: {
navEntry: entry,
pageUrl: entry.name,
},
entries: [entry],
name,
rating: "default",
value,
};
performance.measure(name, {
start: entry.startTime,
duration: entry.duration,
})
log(metric);
}
});
observer2.observe({
type: 'soft-navigation',
buffered: true,
});
</script>
File renamed without changes.
File renamed without changes.

0 comments on commit 9fbb5ba

Please sign in to comment.