fullstory-browser-sdk icon indicating copy to clipboard operation
fullstory-browser-sdk copied to clipboard

Provide a way to defer execution of certain API functions until FullStory is bootstrapped

Open patrick-fs opened this issue 5 years ago • 3 comments

Use a promise-based contract to let integrators know when FullStory is fully bootstrapped:

FullStory.isReady().then(() => {});

or

await FullStory.isReady();
// do more things

OR

use a callback interface for functions that need to wait until FullStory is bootstrapped to become operational: asyncGetCurrentSessionURL(callback, now?)

patrick-fs avatar Aug 06 '19 14:08 patrick-fs

Thoughts on implementation:

isReady() with a promise

let alreadyReady = () => {};
// test to see if window['_fs_ready'] has already been set in the <head> of the document
if (typeof window._fs_ready === 'function') {
  alreadyReady = window._fs_ready;
} 

let readyCalled = false;
window._fs_ready = () => {
  readyCalled = true;
  alreadyReady();
};

export const isReady = new Promise((resolve, reject) => {
  if (readyCalled) return resolve();
  let iteration = 0;
  const id = setInterval(() => {
    if (readyCalled) {
      clearInterval(id);
      return resolve();
    }
    iteration++;
    if (iteration > 10) {
      clearInterval(id);
      return reject('timeout');
    }
  }, 500);
});
  • Using the Promise API will likely require a polyfill (which can be inserted via @babel/preset-env configuration).

async functions with a callback

There's no general isReady() function, rather individual functions that need to wait for FullStory to bootstrap before they become operational have an async{function name} version.

let alreadyReady = () => {};
// test to see if window['_fs_ready'] has already been set in the <head> of the document
if (typeof window._fs_ready === 'function') {
  alreadyReady = window._fs_ready;
} 

let readyCalled = false;
window._fs_ready = () => {
  readyCalled = true;
  alreadyReady();
};

const waitReady = (cb) => {
  if (readyCalled) return cb();
  let iteration = 0;
  const id = setInterval(() => {
    if (readyCalled) {
      clearInterval(id);
      return cb();
    }
    iteration++;
    if (iteration > 10) {
      clearInterval(id);
      throw new Error('timeout');
  }, 500);
}

const export asyncGetCurrentSessionURL(callback, now) {
  const cb = () => {callback(getCurrentSessionURL(now))};
  waitReady(cb);
}
  • Both isReady and async{function name} assume that FullStory is initialized with the init function found in this package and not via placing the snippet tag in the <head> of the document
  • 500 milliseconds could likely be too long of an interval; some analysis is required to find an interval value that doesn't introduce undue latency when retrieving session replay urls while waiting for FullStory to bootstrap

patrick-fs avatar Feb 08 '20 22:02 patrick-fs

Question: is there a use case where a client of the FullStory browser API cares whether FS is ready other than waiting to get a session replay url (or session replay id)?

@bddean would love to know your thoughts.

patrick-fs avatar Feb 09 '20 14:02 patrick-fs

@patrick-fs Sorry, I kept forgetting to reply to this message. No, those are the only two cases, though I can imagine we might want to add more in the future.

Relatedly, here's a PR that adds a callback-based variant to the snippet itself (can't use promises because browser support): https://github.com/cowpaths/mn/pull/19589

bddean avatar Feb 19 '20 21:02 bddean