mw.loader: Make 'mwLoadEnd' less expensive with a single using()
authorTimo Tijhof <krinklemail@gmail.com>
Fri, 26 Aug 2016 04:51:01 +0000 (21:51 -0700)
committerTimo Tijhof <krinklemail@gmail.com>
Wed, 31 Aug 2016 00:39:06 +0000 (17:39 -0700)
20-30ms before this patch, ~2ms after this patch (MacBookPro, Chrome 52).

The creation of 100s of Deferred objects, $.when() tracking them
all, and bubbling up the completion took 20-30ms. This is quite
expensive. Optimise by using a single deferred first.
A module reaching state 'missing' or 'error' is very rare.

Change-Id: I90eea4bfe8fe6d85c395d9d0868bbde482c4a703

resources/src/mediawiki/mediawiki.js

index af37162..7ceb5fe 100644 (file)
                var loading = $.grep( mw.loader.getModuleNames(), function ( module ) {
                        return mw.loader.getState( module ) === 'loading';
                } );
-               // In order to use jQuery.when (which stops early if one of the promises got rejected)
-               // cast any loading failures into successes. We only need a callback, not the module.
-               loading = $.map( loading, function ( module ) {
-                       return mw.loader.using( module ).then( null, function () {
-                               return $.Deferred().resolve();
+               // 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( loading ).then( null, function () {
+                       var all = $.map( loading, function ( module ) {
+                               return mw.loader.using( module ).then( null, function () {
+                                       return $.Deferred().resolve();
+                               } );
                        } );
+                       return $.when.apply( $, all );
                } );
-               $.when.apply( $, loading ).then( function () {
+               loading.then( function () {
                        mwPerformance.mark( 'mwLoadEnd' );
                        mw.hook( 'resourceloader.loadEnd' ).fire();
                } );