Skip to content

Instantly share code, notes, and snippets.

@philipwalton
Created February 16, 2024 23:32
Show Gist options
  • Save philipwalton/4dcc5c228e92b01e77bce58e29b22c15 to your computer and use it in GitHub Desktop.
Save philipwalton/4dcc5c228e92b01e77bce58e29b22c15 to your computer and use it in GitHub Desktop.
A yielding strategy that ensures tasks will still run before the user leaves.
/** A set to keep track of all unresolved yield promises */
const pendingResolvers = new Set();
/**
* Resolves all unresolved yield promises and clears the set.
*/
function resolvePendingPromises() {
for (const resolve of pendingResolvers) {
resolve();
}
pendingResolvers.clear();
}
/**
* Returns a promise that, if the document is visible, will resolve in a new
* task in the next frame. If the document is not visible (or changes to
* hidden prior to the promise resolving), the promise is resolved immediately.
* @return {Promise<void>}
*/
export function yieldUnlessUrgent() {
return new Promise((resolve) => {
pendingResolvers.add(resolve);
if (document.visibilityState === 'visible') {
document.addEventListener('visibilitychange', resolvePendingPromises);
// This code uses rAF+setTimeout(0) because these APIs are well-supported,
// but you could replace this with any technique you like,
// (e.g. scheduler.yield or scheduler.postTask, with fallback).
return requestAnimationFrame(() => {
setTimeout(() => {
pendingResolvers.delete(resolve);
resolve();
}, 0);
});
}
// Still here? Resolve immediately.
resolvePendingPromises();
});
}
@philipwalton
Copy link
Author

Usage for this function would look something like this:

import {yieldUnlessUrgent} from './yieldUnlessUrgent.js';

document.addEventListener('click', (event) => {
  // Handle the click event in some way...
  doSomeImportantUIWork(event);

  // Then yield before running any analytics code...
  await yieldUnlessUrgent();

  // This code will run even if the click is on a link that is navigating away.
  gtag('send', 'event', {...});
});

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment