resourceloader: Simplify mwLoadEnd hook implementation
authorTimo Tijhof <krinklemail@gmail.com>
Mon, 7 May 2018 21:57:40 +0000 (22:57 +0100)
committerKrinkle <krinklemail@gmail.com>
Sat, 19 May 2018 10:51:35 +0000 (10:51 +0000)
In addition to the high overhead of $.Deferred per-module in
mw.loader.using(), this was also using $.when() and another
Deferred in catch() for casting.

The handler for marking mwLoadEnd needs a Promise in our fallback
for compatibility with the original using() call. But the code
within our fallback does not need to use N promises, for that
we can simply use a counter that we decrement, given we only need
to worry about completion.

So basically, use `Deferred#always(callback)`
instead of `Deferred.catch(=>Defered().resolve)).then(callback)`.

This is in preparation for moving the code to NavigationTiming.

Change-Id: I20514d3fe680fc9384a0f7ce0880652970d86856

resources/src/mediawiki/mediawiki.js

index 486fb80..9837ce8 100644 (file)
         * @member mw.hook
         */
        $( function () {
-               var loading, modules;
-
-               modules = mw.loader.getModuleNames().filter( function ( module ) {
+               // Get a list of modules currently in loading state
+               var modules = mw.loader.getModuleNames().filter( function ( module ) {
                        return mw.loader.getState( module ) === 'loading';
                } );
-               // We only need a callback, not any actual module. First try a single using()
-               // for all loading modules. If one fails, fall back to tracking each module
-               // separately via $.when(), this is expensive.
-               loading = mw.loader.using( modules ).catch( function () {
-                       var all = modules.map( function ( module ) {
-                               return mw.loader.using( module ).catch( function () {
-                                       return $.Deferred().resolve();
-                               } );
+               // Wait for them to complete loading (regardles of failures). First, try a single
+               // mw.loader.using() call. That's efficient, but has the drawback of being rejected
+               // upon first failure. Fall back to tracking each module separately. We usually avoid
+               // that because of high overhead for that internally to mw.loader.
+               mw.loader.using( modules ).catch( function () {
+                       return $.Deferred( function ( deferred ) {
+                               var i, count = modules.length;
+                               function decrement() {
+                                       count--;
+                                       if ( count === 0 ) {
+                                               deferred.resolve();
+                                       }
+                               }
+                               for ( i = 0; i < modules.length; i++ ) {
+                                       mw.loader.using( modules[ i ] ).always( decrement );
+                               }
                        } );
-                       return $.when.apply( $, all );
-               } );
-               loading.then( function () {
+               } ).then( function () {
                        if ( window.performance && performance.mark ) {
                                performance.mark( 'mwLoadEnd' );
                        }