5

I have found this excellent snippet Make my userscript wait for other scripts to load that shows me how to wait for a function to be available before calling it.

Currently I have this local code in my script which I have put together which works for me

waitForFnc();

function waitForFnc() {
    if (typeof Portal.Management_Init == "undefined") {
        window.setTimeout(waitForFnc, 50);
    }
    else {
        Portal.Management_Init();
    }
}

However, I would like to write a generic version of 'waitForFnc' as I need to do the same thing in several places. Something like

waitForFnc(Portal.Management_Init);

function waitForFnc(fnc) {
    if (typeof fnc == "undefined") {
        window.setTimeout(waitForFnc(fnc), 50);
    }
    else {
       fnc();
    }
}

where I pass the name of the function in which is called when it becomes available. The above code does not work but I am unsure as to how to resolve it.

Regards Paul

Community
  • 1
  • 1
Paul Marsden
  • 151
  • 11

4 Answers4

5

There are some potential problems with what you are trying to do. If you call waitForFnc() before Portal is even defined, you will get a null property access exception. If you are trying for a truly generic solution, you will probably have to use eval() *gasp*

While we're at it, let's add support for passing arguments to the function we're waiting on.

function waitForFn(fnName, args){
    var fn;
    try{
        eval("fn = " + fnName);
        if(fn){
            fn.apply(null, args);
        }else{
            setTimeout(function(){waitForFn(fnName, args);}, 50);
        }
    }catch(e){
        setTimeout(function(){waitForFn(fnName, args);}, 50);
    }
}

waitForFn("Portal.Management_Init", [arg0, arg1]);
jordancpaul
  • 2,954
  • 1
  • 18
  • 27
  • Despite I think, in my case, the best solution would be to be sure that file importing containing the function should be done at top of the scripts imports or even on the head section, for my case we're talking about a bundle that contains a lot of other third party libraries, so instead of extracting this library into a separated importing this approach seems to be very suitable to me and it worked nicely. Thank you for your answer. – André Andrade Nov 28 '22 at 15:19
3

Basically, when this line of code is executed: window.setTimeout(waitForFnc(fnc), 50);, the "waitForFnc" is evaluated before the timeout is set. While you need to pass the calling statement as a parameter.

Here's how you do that:

window.setTimeout(function() {waitForFnc(fnc);}, 50);

What this does, it defines a function, the same way as if you'd write it into the variable:

var myFunc = function() {
    waitForFnc(fnc);
};

This function is not yet executed, it is only defined. Then you pass it into the "setTimeout":

window.setTimeout(myFunc, 50);

Which makes the "setTimeout" to execute that function after 50msec. And when it does, it will call waitForFnc(fnc).

bezmax
  • 25,562
  • 10
  • 53
  • 84
0

In your code replace:

window.setTimeout(waitForFnc(fnc), 50);

with closure:

window.setTimeout(function() {waitForFnc(fnc)}, 50);

But may I ask why do you need such a weird code? I would rather expect to have an API allowing to register some callback:

Portal.onManagementInitAvailable(fn);
Tomasz Nurkiewicz
  • 334,321
  • 69
  • 703
  • 674
  • 1
    Clearly your expectations would not be met here. – Chuck Apr 26 '12 at 07:37
  • I am usign ASP MVC and I have a partial views that load dynamically with ajax. Within the main partial view there are other partial views that become visible from time to time and need to use common javascript that is loaded dynamically by the parent partial view. On initial page load, the sub-partial views need to wait to make sure that the script has been loaded before calling the initialise routines. – Paul Marsden Apr 26 '12 at 12:17
0

Depending on what you're loading, you might be able to leverage require.js to take care of this for you; that's basically what it's for: http://requirejs.org/docs/why.html#9

Yusuf X
  • 14,513
  • 5
  • 35
  • 47