Jump to content

Topic on Talk:ResourceLoader/Core modules

Is there any way to callback after using load?

11
Eduemoni (talkcontribs)

I am trying to load a js file under my wiki

mw.loader.load( '/w/index.php?title=MediaWiki:Slick.js&action=raw&ctype=text/javascript' );


It loads the Slick jquery plugin, then after loading it I would like to use it as a gadget, where a wildcard number of classes can become a caroussel, however after loading it I am unable to use it, because the module is not loaded yet

Od1n (talkcontribs)

You could try the following:

mw.loader.implement( 'HackyCustomModuleRegistration', [ '/w/index.php?title=MediaWiki:Slick.js&action=raw&ctype=text/javascript' ] );
mw.loader.using( 'HackyCustomModuleRegistration', function () {
    // ...
} );

However, the mw.loader.implement() method (code) is missing from the documentation, and is to be considered private.

Jack who built the house (talkcontribs)

Won't this work for you?

$.getScript( 'https://.../w/index.php?title=MediaWiki:Slick.js&action=raw&ctype=text/javascript' ).done( function () {
    // your code
} );
Erutuon (talkcontribs)

Or even shorter:

$.getScript( 'https://.../w/index.php?title=MediaWiki:Slick.js&action=raw&ctype=text/javascript', function () {
    // runs if loading was successful
} );

Od1n (talkcontribs)

I thought it was a better solution… but from the jQuery.getScript() documentation (emphasis mine):

« The callback is fired once the script has been loaded but not necessarily executed. »

Erutuon (talkcontribs)

So if $.getScript(url, func) doesn't necessarily run the script before calling func, is the script run before func with $.getScript(url).done(func) or with the mw.loader version? I've used the $.getScript version that I posted without considering this issue; for whatever reason it worked fine.

Od1n (talkcontribs)

Digging inside the code of module mediawiki (and mediawiki.base), I found out the "load+execute script, then callback" code is actually very simple, see:

function addScript( src, callback ) {
    var script = document.createElement( 'script' );
    script.src = src;
    script.onload = script.onerror = function () {
        if ( script.parentNode ) {
            script.parentNode.removeChild( script );
        }
        script = null;
        if ( callback ) {
            callback();
            callback = null;
        }
    };
    document.head.appendChild( script );
}

A few notes about the code:

  • The two = null are for performance, to free memory (otherwise the objects would stay in memory forever)
  • The onerror event is not for request error, but for script execution error


So, I think the best solution would be to reuse the above snippet as standalone. Lightweight, no dependency of any kind :)

Od1n (talkcontribs)

For the sake of refs'ing, this implementation has been introduced in this commit (T192623).

Yug (talkcontribs)

I also tried with asynch, await and .then() but I was informed that [As of April 2021] the JS syntax checker blocks use of ES6 in (i) gadgets, (ii) sitewide JS pages and (iii) user common.js page. So no `await` in `MediaWiki:Common.js`.

I would like to load into `MediaWiki:Common.js` 3 data from pages [MediaWiki:dataA.js], [MediaWiki:dataB.js], [MediaWiki:dataC.js] , then process these data. Clean, merge. Then create a dataviz. I need it to be sequential but I'm still not clear how to do so. As of now, this data are still undefined when my clean, merge, and dataviz code runs.

As for this ES6 issue, there is a hack consisting in storing the ES6 in the User namespace (ex: `User:.../something.js`), then you can use mw.loader.load() and it somehow work.

Od1n (talkcontribs)

You may also use mw.loader.getScript() (which, contrarily to jQuery.getScript(), wait for the loaded JavaScript to be executed):

mw.loader.getScript( '/w/index.php?title=MediaWiki:Slick.js&action=raw&ctype=text/javascript' ).then( function () {
    /* Callback */
} );

Internally, it uses jQuery.ajax(), just tweaking the options so that the HTTP cache is re-enabled. I had a look at jQuery source code some time ago, to confirm it is executing the loaded JavaScript before triggering the promise.

Od1n (talkcontribs)

I'd like to clarify a confusion that was there initially, but has been addressed for good since 2022:

Whether you do:

$.getScript( url ).done( callback );

$.getScript( url, callback );

mw.loader.getScript( url ).then( callback );

In all cases, the script is guaranteed to be loaded and executed before the callback is called.

See jQuery issue #1207 for details / investigations.

The only difference is that mw.loader.getScript() passes an option cache: true to the internally used $.ajax().

Reply to "Is there any way to callback after using load?"