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

Guard against detection logic from negatively impacting INP #894

Closed
westonruter opened this issue Dec 5, 2023 · 1 comment
Closed

Guard against detection logic from negatively impacting INP #894

westonruter opened this issue Dec 5, 2023 · 1 comment
Labels
[Plugin] Optimization Detective Issues for the Optimization Detective plugin

Comments

@westonruter
Copy link
Member

westonruter commented Dec 5, 2023

Feature Description

As discussed in #878 (comment), the Image Loading Optimization module (see #869) has detection logic to discover the LCP image and other images which are in the initial viewport. This logic is currently running at the load event after which it immediately sends the data to the REST API endpoint:

// Wait until the resources on the page have fully loaded.
await new Promise( ( resolve ) => {
if ( doc.readyState === 'complete' ) {
resolve();
} else {
win.addEventListener( 'load', resolve, { once: true } );
}
} );
// Stop observing.
disconnectIntersectionObserver();
if ( isDebug ) {
log( 'Detection is stopping.' );
}
/** @type {URLMetrics} */
const urlMetrics = {
url: win.location.href,
slug: urlMetricsSlug,
nonce: urlMetricsNonce,
viewport: {
width: win.innerWidth,
height: win.innerHeight,
},
elements: [],
};
const lcpMetric = lcpMetricCandidates.at( -1 );
for ( const elementIntersection of elementIntersections ) {
const breadcrumbs = breadcrumbedElementsMap.get(
elementIntersection.target
);
if ( ! breadcrumbs ) {
if ( isDebug ) {
error( 'Unable to look up breadcrumbs for element' );
}
continue;
}
const isLCP =
elementIntersection.target === lcpMetric?.entries[ 0 ]?.element;
/** @type {ElementMetrics} */
const elementMetrics = {
isLCP,
isLCPCandidate: !! lcpMetricCandidates.find(
( lcpMetricCandidate ) =>
lcpMetricCandidate.entries[ 0 ]?.element ===
elementIntersection.target
),
breadcrumbs,
intersectionRatio: elementIntersection.intersectionRatio,
intersectionRect: elementIntersection.intersectionRect,
boundingClientRect: elementIntersection.boundingClientRect,
};
urlMetrics.elements.push( elementMetrics );
}
if ( isDebug ) {
log( 'URL metrics:', urlMetrics );
}
// TODO: Wait until idle? Yield to main?
try {
const response = await fetch( restApiEndpoint, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-WP-Nonce': restApiNonce,
},
body: JSON.stringify( urlMetrics ),
} );
if ( response.status === 200 ) {
setStorageLock( getCurrentTime() );
}
if ( isDebug ) {
const body = await response.json();
if ( response.status === 200 ) {
log( 'Response:', body );
} else {
error( 'Failure:', body );
}
}
} catch ( err ) {
if ( isDebug ) {
error( err );
}
}

However, given that the metrics collection is a low priority task which is intended to improve the user experience, we must guard against it from possibly impacting a visitor in a negative way when URL metrics need to gathered. For this reason there is the following todo:

// TODO: Wait until idle? Yield to main?

We should leverage requestIdleCallback() and/or scheduler.yield() (or else setTimeout)--whatever is available--to make sure that the detection task is broken up into small tasks that do not contribute to a long task.

@westonruter
Copy link
Member Author

Completed in #978.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
[Plugin] Optimization Detective Issues for the Optimization Detective plugin
Projects
None yet
Development

No branches or pull requests

1 participant