From 2a895edbd23f796206e26fb7d08a79fe6c653103 Mon Sep 17 00:00:00 2001 From: Timo Tijhof Date: Mon, 17 Oct 2016 19:01:14 +0100 Subject: [PATCH] resourceloader: Make cache-eval in mw.loader.work asynchronous This is an amended version of reverted commit 482ad8d9fb2e. The difference is that this change is smaller. We keep the eval as one large chunk. Not split up in <=50ms chunks and spread out based on how fast code executes on the device. This means we keep the issue of an irresponsive UI thread during execution, and trade it for finishing quicker. This doesn't feel right, but is something we should change separately, later because it has side-effects (T146510). Doing it in one chunk is the status quo as we've had it for the last two years. This commit merely defers the big eval chunk to one tick later so that higher priority work may happen first (e.g. rendering). Bug: T142129 Change-Id: I2b31b27554cd29b48d986ed6a6f698a77e59c0e5 --- resources/src/mediawiki/mediawiki.js | 49 +++++++++++++++++++--------- 1 file changed, 33 insertions(+), 16 deletions(-) diff --git a/resources/src/mediawiki/mediawiki.js b/resources/src/mediawiki/mediawiki.js index f878e42580..6b234399d4 100644 --- a/resources/src/mediawiki/mediawiki.js +++ b/resources/src/mediawiki/mediawiki.js @@ -1680,6 +1680,26 @@ } } + /** + * @private + * @param {string[]} implementations Array containing pieces of JavaScript code in the + * form of calls to mw.loader#implement(). + * @param {Function} cb Callback in case of failure + * @param {Error} cb.err + */ + function asyncEval( implementations, cb ) { + if ( !implementations.length ) { + return; + } + mw.requestIdleCallback( function () { + try { + $.globalEval( implementations.join( ';' ) ); + } catch ( err ) { + cb( err ); + } + } ); + } + /** * Make a versioned key for a specific module. * @@ -1733,7 +1753,7 @@ * @protected */ work: function () { - var q, batch, concatSource, origBatch; + var q, batch, implementations, sourceModules; batch = []; @@ -1763,19 +1783,18 @@ mw.loader.store.init(); if ( mw.loader.store.enabled ) { - concatSource = []; - origBatch = batch; + implementations = []; + sourceModules = []; batch = $.grep( batch, function ( module ) { - var source = mw.loader.store.get( module ); - if ( source ) { - concatSource.push( source ); + var implementation = mw.loader.store.get( module ); + if ( implementation ) { + implementations.push( implementation ); + sourceModules.push( module ); return false; } return true; } ); - try { - $.globalEval( concatSource.join( ';' ) ); - } catch ( err ) { + asyncEval( implementations, function ( err ) { // Not good, the cached mw.loader.implement calls failed! This should // never happen, barring ResourceLoader bugs, browser bugs and PEBKACs. // Depending on how corrupt the string is, it is likely that some @@ -1786,16 +1805,14 @@ // something that infected the implement call itself, don't take any // risks and clear everything in this cache. mw.loader.store.clear(); - // Re-add the ones still pending back to the batch and let the server - // repopulate these modules to the cache. - // This means that at most one module will be useless (the one that had - // the error) instead of all of them. + mw.track( 'resourceloader.exception', { exception: err, source: 'store-eval' } ); - origBatch = $.grep( origBatch, function ( module ) { + // Re-add the failed ones that are still pending back to the batch + var failed = $.grep( sourceModules, function ( module ) { return registry[ module ].state === 'loading'; } ); - batch = batch.concat( origBatch ); - } + batchRequest( failed ); + } ); } batchRequest( batch ); -- 2.20.1