32

I have an issue. One of my JS scripts needs Facebook SDK and Twitter widgets JS to load first. Facebook creates FB object, Twitter creates twttr object. Both of them create these objects AFTER my script fires, even though they're loaded from <head>.

I think solution is to periodically check if FB and twttr are defined, and then proceed with executing my script. But I have no idea how to do this.

I tried creating a loop

while (typeof FB === 'undefined' || typeof twttr === 'undefined' || typeof twttr.widgets === 'undefined') {
    // run timeout for 100 ms with noop inside
}

But this clearly does not work as it keeps firing timeouts at a high speed and page hangs.

Please help me, I can't sleep because of this issue.

Wayne
  • 59,728
  • 15
  • 131
  • 126
mvbl fst
  • 5,213
  • 8
  • 42
  • 59
  • 1
    I think there must be something wrong... If you put ALL you scripts inside the head tag, they will be loaded in the same order you want them... so it's enough to write FB and twitter scripts before you script... that should be all – andreapier Dec 23 '11 at 16:47
  • +andreapier -- they are not ALL loaded inside . If you read instructions for Facebook JS SDK, than you'll see the code they provide must be the first thing inside . That's one example. – mvbl fst Jan 13 '12 at 22:24

4 Answers4

53

If the scripts are loaded in the normal, synchronous way, then just make sure that your <script> include appears after the library scripts in the document's <head>. If, on the other hand, those scripts are loading objects asynchronously (as seems to be the case), then create something like this:

function whenAvailable(name, callback) {
    var interval = 10; // ms
    window.setTimeout(function() {
        if (window[name]) {
            callback(window[name]);
        } else {
            whenAvailable(name, callback);
        }
    }, interval);
}

And use it like this:

whenAvailable("twttr", function(t) {
    // do something
});

The function given in the second argument to whenAvailable will not execute until twttr is defined on the global window object. You can do the same thing for FB.

Important note: Those libraries probably also provide some built-in way to execute code after they have loaded. You should look for such hooks in their respective documentation.

Wayne
  • 59,728
  • 15
  • 131
  • 126
  • 2
    Thanks a lot. I used your function but had to modify condition to "if (typeof eval(name) !== 'undefined')" because it would not work on names like 'twttr.widgets' – mvbl fst Dec 23 '11 at 17:19
  • You shouldn't use eval() though – LasagnaAndroid Jan 06 '15 at 20:22
  • 6
    Rather use `window.setInteval` and `window.clearInterval` than `window.setTimeout` and recursion: https://jsfiddle.net/2bubub2j/ because if you choose to `use strict` then you'll have issues with the function arguments object. – Jani Hyytiäinen Feb 02 '16 at 08:17
  • Certainly you wouldn't need multiple setTimeouts or anonymous functions. This can be quite easily simplified by removing the `window.setTimeout(function() {` ... `}, interval);` wrapper, unless you want it to run asynchronously on first load, in which case you can replace `window.setTimeout(arguments.callee, interval);` with `whenAvailable(name, callback);`. – Shaun Cockerill Jan 19 '18 at 01:33
  • "arguments.callee" should not be used anymore. EcmaScript 5 forbids the use of arguments.callee in strict mode. – Rodrigo De Almeida Siqueira Jul 04 '20 at 18:29
  • Updated to remove `arguments.callee` and the second `setTimeout` – Wayne Jul 06 '20 at 00:22
  • 1
    @Wayne This will run forever if browser blocked twttr or fb url to prevent analytics or fail for some reason – gidiwe2427 Jan 13 '22 at 16:15
8

Have you put your script to be executed on page load? (ie. body onload="do_this ();")

That should make your code execute once all external resources has been loaded.


Regarding the use of setTimeout

setTimeout will return immediately, if you'd like to wait for certain variable to be defined, use something as the below.

function when_external_loaded (callback) {
  if (typeof FB === 'undefined' || typeof twtter === 'undefined') {
    setTimeout (function () {
       when_external_loaded (callback);
    }, 100); // wait 100 ms
  } else { callback (); }
}

...

when_external_loaded (function () {
    alert (FB);
    alert (twtter);
});
Community
  • 1
  • 1
Filip Roséen - refp
  • 62,493
  • 20
  • 150
  • 196
2

const checkIfLoaded = ('lib', cb) => {
  const interval = setInterval(() => {
    if (lib) {
      typeof cb === 'function' && cb();
      clearInterval(interval);
    } else {
      console.log('not yet');
    }
  }, 100);
}
1

If the Facebook scripts are being loaded asynchronously, Facebook has a supported way to execute code when it's library loads which should be much more efficient than polling for it. See this answer for an example: https://stackoverflow.com/a/5336483/816620.

If the Facebook scripts are being loaded synchronously, then you don't have to wait for them - they will load before any other scripts after them run.

Community
  • 1
  • 1
jfriend00
  • 683,504
  • 96
  • 985
  • 979
  • Well yes with FB it's a bit easier as it's explicitly initiated but you don't have any control over Twitter code. – mvbl fst Dec 23 '11 at 17:21