mw.loader: Use requestIdleCallback to update module store
authorTimo Tijhof <krinklemail@gmail.com>
Tue, 10 Nov 2015 22:09:21 +0000 (22:09 +0000)
committerTimo Tijhof <krinklemail@gmail.com>
Tue, 10 Nov 2015 22:09:49 +0000 (22:09 +0000)
.. instead of setTimeout.

Also adapt the code accordingly since it is no longer needed to
cancel and re-schedule an update every time update() is called.
This is already handled by requestIdleCallback.

We can let the already-scheduled callback survive. The data
that will be saved is serialised during the flush anyway,
not when scheduling.

Change-Id: Iea48919b4baba7647040b6ed9ff4b6d0d387b962

resources/src/mediawiki/mediawiki.js

index 3ffe55f..db07ec9 100644 (file)
                                        },
 
                                        /**
-                                        * Sync modules to localStorage.
+                                        * Sync in-memory store back to localStorage.
                                         *
-                                        * This function debounces localStorage updates. When called multiple times in
-                                        * quick succession, the calls are coalesced into a single update operation.
-                                        * This allows us to call #update without having to consider the module load
-                                        * queue; the call to localStorage.setItem will be naturally deferred until the
-                                        * page is quiescent.
+                                        * This function debounces updates. When called with a flush already pending,
+                                        * the call is coalesced into the pending update. The call to
+                                        * localStorage.setItem will be naturally deferred until the page is quiescent.
                                         *
-                                        * Because localStorage is shared by all pages with the same origin, if multiple
+                                        * Because localStorage is shared by all pages from the same origin, if multiple
                                         * pages are loaded with different module sets, the possibility exists that
                                         * modules saved by one page will be clobbered by another. But the impact would
                                         * be minor and the problem would be corrected by subsequent page views.
                                         * @method
                                         */
                                        update: ( function () {
-                                               var timer;
+                                               var hasPendingWrite = false;
 
-                                               function flush() {
-                                                       var data,
-                                                               key = mw.loader.store.getStoreKey();
-
-                                                       if ( !mw.loader.store.enabled ) {
-                                                               return false;
+                                               function flushWrites() {
+                                                       var data, key;
+                                                       if ( !hasPendingWrite || !mw.loader.store.enabled ) {
+                                                               return;
                                                        }
+
                                                        mw.loader.store.prune();
+                                                       key = mw.loader.store.getStoreKey();
                                                        try {
                                                                // Replacing the content of the module store might fail if the new
                                                                // contents would exceed the browser's localStorage size limit. To
                                                        } catch ( e ) {
                                                                mw.track( 'resourceloader.exception', { exception: e, source: 'store-localstorage-update' } );
                                                        }
+
+                                                       hasPendingWrite = false;
                                                }
 
                                                return function () {
-                                                       clearTimeout( timer );
-                                                       timer = setTimeout( flush, 2000 );
+                                                       if ( !hasPendingWrite ) {
+                                                               hasPendingWrite = true;
+                                                               mw.requestIdleCallback( flushWrites );
+                                                       }
                                                };
                                        }() )
                                }