Skip to content

Commit

Permalink
Merge pull request #278 from GoogleChrome/safari-observe-bug
Browse files Browse the repository at this point in the history
Defer execution of observer callbacks
  • Loading branch information
philipwalton committed Nov 14, 2022
2 parents 7f0ed0b + 3595ee4 commit 049e669
Show file tree
Hide file tree
Showing 2 changed files with 11 additions and 20 deletions.
7 changes: 6 additions & 1 deletion src/lib/observe.ts
Expand Up @@ -43,7 +43,12 @@ export const observe = <K extends keyof PerformanceEntryMap>(
try {
if (PerformanceObserver.supportedEntryTypes.includes(type)) {
const po = new PerformanceObserver((list) => {
callback(list.getEntries() as PerformanceEntryMap[K]);
// Delay by a microtask to workaround a bug in Safari where the
// callback is invoked immediately, rather than in a separate task.
// See: https://github.com/GoogleChrome/web-vitals/issues/277
Promise.resolve().then(() => {
callback(list.getEntries() as PerformanceEntryMap[K]);
});
});
po.observe(Object.assign({
type,
Expand Down
24 changes: 5 additions & 19 deletions src/onFCP.ts
Expand Up @@ -42,9 +42,7 @@ export const onFCP = (onReport: FCPReportCallback, opts?: ReportOpts) => {
const handleEntries = (entries: FCPMetric['entries']) => {
(entries as PerformancePaintTiming[]).forEach((entry) => {
if (entry.name === 'first-contentful-paint') {
if (po) {
po.disconnect();
}
po!.disconnect();

// Only report if the page wasn't hidden prior to the first paint.
if (entry.startTime < visibilityWatcher.firstHiddenTime) {
Expand All @@ -60,23 +58,11 @@ export const onFCP = (onReport: FCPReportCallback, opts?: ReportOpts) => {
});
};

// TODO(philipwalton): remove the use of `fcpEntry` once this bug is fixed.
// https://bugs.webkit.org/show_bug.cgi?id=225305
// The check for `getEntriesByName` is needed to support Opera:
// https://github.com/GoogleChrome/web-vitals/issues/159
// The check for `window.performance` is needed to support Opera mini:
// https://github.com/GoogleChrome/web-vitals/issues/185
const fcpEntry = window.performance && window.performance.getEntriesByName &&
window.performance.getEntriesByName('first-contentful-paint')[0];

const po = fcpEntry ? null : observe('paint', handleEntries);

if (fcpEntry || po) {
report = bindReporter(onReport, metric, thresholds, opts.reportAllChanges);
const po = observe('paint', handleEntries);

if (fcpEntry) {
handleEntries([fcpEntry]);
}
if (po) {
report = bindReporter(
onReport, metric, thresholds, opts!.reportAllChanges);

// Only report after a bfcache restore if the `PerformanceObserver`
// successfully registered or the `paint` entry exists.
Expand Down

0 comments on commit 049e669

Please sign in to comment.