Просмотр исходного кода

Оптимизация загрузки и хранения списка недавних книг

Book Pauk 4 лет назад
Родитель
Сommit
d06d20a33e

+ 2 - 2
client/components/Reader/ServerStorage/ServerStorage.vue

@@ -507,10 +507,10 @@ class ServerStorage extends Vue {
                 if (md.key && result[md.key])
                     result[md.key] = utils.applyObjDiff(result[md.key], md.mod, {isAddChanged: true});
 
-                if (!bookManager.loaded) {
+                /*if (!bookManager.loaded) {
                     this.warning('Ожидание загрузки списка книг перед синхронизацией');
                     while (!bookManager.loaded) await utils.sleep(100);
-                }
+                }*/
 
                 if (newRecent.rev != this.cachedRecent.rev)
                     await this.setCachedRecent(newRecent);

+ 126 - 80
client/components/Reader/share/bookManager.js

@@ -17,9 +17,12 @@ const bmDataStore = localForage.createInstance({
 });
 
 //список недавно открытых книг
-const bmRecentStore = localForage.createInstance({
+const bmRecentStoreOld = localForage.createInstance({
     name: 'bmRecentStore'
 });
+const bmRecentStoreNew = localForage.createInstance({
+    name: 'bmRecentStoreNew'
+});
 
 class BookManager {
     async init(settings) {
@@ -28,15 +31,74 @@ class BookManager {
 
         this.eventListeners = [];
         this.books = {};
+
         this.recent = {};
+        this.saveRecent = _.debounce(() => {
+            bmRecentStoreNew.setItem('recent', this.recent);
+        }, 300, {maxWait: 800});
+
+        this.saveRecentItem = _.debounce(() => {
+            bmRecentStoreNew.setItem('recent-item', this.recentItem);
+            this.recentRev++;
+            bmRecentStoreNew.setItem('rev', this.recentRev);
+        }, 200, {maxWait: 300});
+
+        //загрузка bmRecentStore
+        this.recentRev = await bmRecentStoreNew.getItem('rev') || 0;
+        if (this.recentRev) {
+            this.recent = await bmRecentStoreNew.getItem('recent');
+            if (!this.recent)
+                this.recent = {};
+
+            this.recentItem = await bmRecentStoreNew.getItem('recent-item');
+            if (this.recentItem)
+                this.recent[this.recentItem.key] = this.recentItem;
+
+            this.recentLastKey = await bmRecentStoreNew.getItem('recent-last-key');
+            if (this.recentLastKey) {
+                const meta = await bmMetaStore.getItem(`bmMeta-${this.recentLastKey}`);
+                if (_.isObject(meta)) {
+                    this.books[meta.key] = meta;
+                }
+            }
+
+            await this.cleanRecentBooks();
 
-        this.recentLast = await bmRecentStore.getItem('recent-last');
-        if (this.recentLast) {
-            this.recent[this.recentLast.key] = this.recentLast;
-            const meta = await bmMetaStore.getItem(`bmMeta-${this.recentLast.key}`);
-            if (_.isObject(meta)) {
-                this.books[meta.key] = meta;
+        } else {//TODO: убрать после 06.2021, когда bmRecentStoreOld устареет
+            this.recentLast = await bmRecentStoreOld.getItem('recent-last');
+            if (this.recentLast) {
+                this.recent[this.recentLast.key] = this.recentLast;
+                const meta = await bmMetaStore.getItem(`bmMeta-${this.recentLast.key}`);
+                if (_.isObject(meta)) {
+                    this.books[meta.key] = meta;
+                }
+            }
+
+            let key = null;
+            const len = await bmRecentStoreOld.length();
+            for (let i = len - 1; i >= 0; i--) {
+                key = await bmRecentStoreOld.key(i);
+                if (key) {
+                    let r = await bmRecentStoreOld.getItem(key);
+                    if (_.isObject(r) && r.key) {
+                        this.recent[r.key] = r;
+                    }
+                } else  {
+                    await bmRecentStoreOld.removeItem(key);
+                }
             }
+
+            //размножение для дебага
+            /*if (key) {
+                for (let i = 0; i < 1000; i++) {
+                    const k = this.keyFromUrl(i.toString());
+                    this.recent[k] = Object.assign({}, _.cloneDeep(this.recent[key]), {key: k, touchTime: Date.now() - 1000000, url: utils.randomHexString(300)});
+                }
+            }*/
+
+            await bmRecentStoreNew.setItem('recent', this.recent);
+            this.recentRev = 1;
+            await bmRecentStoreNew.setItem('rev', this.recentRev);
         }
 
         this.recentChanged = true;
@@ -44,9 +106,7 @@ class BookManager {
         this.loadStored();//no await
     }
 
-    //Долгая асинхронная загрузка из хранилища.
-    //Хранение в отдельных записях дает относительно
-    //нормальное поведение при нескольких вкладках с читалкой в браузере.
+    //Ленивая асинхронная загрузка bmMetaStore
     async loadStored() {
         //даем время для загрузки последней читаемой книги, чтобы не блокировать приложение
         await utils.sleep(2000);
@@ -73,32 +133,7 @@ class BookManager {
             }
         }
 
-        let key = null;
-        len = await bmRecentStore.length();
-        for (let i = len - 1; i >= 0; i--) {
-            key = await bmRecentStore.key(i);
-            if (key) {
-                let r = await bmRecentStore.getItem(key);
-                if (_.isObject(r) && r.key) {
-                    this.recent[r.key] = r;
-                }
-            } else  {
-                await bmRecentStore.removeItem(key);
-            }
-        }
-
-        //размножение для дебага
-        /*if (key) {
-            for (let i = 0; i < 1000; i++) {
-                const k = this.keyFromUrl(i.toString());
-                this.recent[k] = Object.assign({}, _.cloneDeep(this.recent[key]), {key: k, touchTime: Date.now() - 1000000, url: utils.randomHexString(300)});
-            }
-        }*/
-        
         await this.cleanBooks();
-        await this.cleanRecentBooks();
-
-        this.recentChanged = true;
         this.loaded = true;
         this.emit('load-stored-finish');
     }
@@ -331,6 +366,43 @@ class BookManager {
     }
 
     //-- recent --------------------------------------------------------------
+    async recentSetItem(item = null, skipCheck = false) {
+        const rev = await bmRecentStoreNew.getItem('rev');
+        if (rev != this.recentRev && !skipCheck) {
+            const newRecent = await bmRecentStoreNew.getItem('recent');
+            Object.assign(this.recent, newRecent);
+            this.recentItem = await bmRecentStoreNew.getItem('recent-item');
+            this.recentRev = rev;
+        }
+
+        const prevKey = (this.recentItem ? this.recentItem.key : '');
+        if (item) {
+            this.recent[item.key] = item;
+            this.recentItem = item;
+        } else {
+            this.recentItem = null;
+        }
+
+        this.saveRecentItem();
+
+        if (!item || prevKey != item.key) {
+            this.saveRecent();
+        }
+
+        this.recentChanged = true;
+
+        if (item) {
+            this.emit('recent-changed', item.key);
+        } else {
+            this.emit('recent-changed');
+        }
+    }
+
+    async recentSetLastKey(key) {
+        this.recentLastKey = key;
+        await bmRecentStoreNew.setItem('recent-last-key', this.recentLastKey);
+    }
+
     async setRecentBook(value) {
         const result = this.metaOnly(value);
         result.touchTime = Date.now();
@@ -344,38 +416,25 @@ class BookManager {
                 result.bookPosSeen = this.recent[result.key].bookPosSeen;
         }
 
-        this.recent[result.key] = result;
-
-        await bmRecentStore.setItem(result.key, result);
-
-        this.recentLast = result;
-        await bmRecentStore.setItem('recent-last', this.recentLast);
-
-        this.recentChanged = true;
-        this.emit('recent-changed', result.key);
+        await this.recentSetLastKey(result.key);
+        await this.recentSetItem(result);
         return result;
     }
 
     async getRecentBook(value) {
-        let result = this.recent[value.key];
-        if (!result) {
-            result = await bmRecentStore.getItem(value.key);
-            if (result)
-                this.recent[value.key] = result;
-        }
-        return result;
+        return this.recent[value.key];
     }
 
     async delRecentBook(value) {
-        this.recent[value.key].deleted = 1;
-        await bmRecentStore.setItem(value.key, this.recent[value.key]);
+        const item = this.recent[value.key];
+        item.deleted = 1;
 
-        if (this.recentLast.key == value.key) {
-            this.recentLast = null;
-            await bmRecentStore.setItem('recent-last', this.recentLast);
+        if (this.recentLastKey == value.key) {
+            await this.recentSetLastKey(null);
         }
+
+        await this.recentSetItem(item);
         this.emit('recent-deleted', value.key);
-        this.emit('recent-changed', value.key);
     }
 
     async cleanRecentBooks() {
@@ -383,24 +442,22 @@ class BookManager {
 
         let isDel = false;
         for (let i = 1000; i < sorted.length; i++) {
-            await bmRecentStore.removeItem(sorted[i].key);
             delete this.recent[sorted[i].key];
-            await bmRecentStore.removeItem(sorted[i].key);
             isDel = true;
         }
 
         this.sortedRecentCached = null;
 
         if (isDel)
-            this.emit('recent-changed');
+            await this.recentSetItem();
         return isDel;
     }
 
     mostRecentBook() {
-        if (this.recentLast) {
-            return this.recentLast;
+        if (this.recentLastKey) {
+            return this.recent[this.recentLastKey];
         }
-        const oldRecentLast = this.recentLast;
+        const oldKey = this.recentLastKey;
 
         let max = 0;
         let result = null;
@@ -411,10 +468,11 @@ class BookManager {
                 result = book;
             }
         }
-        this.recentLast = result;
-        bmRecentStore.setItem('recent-last', this.recentLast);//no await
+        
+        const newRecentLastKey = (result ? result.key : null);
+        this.recentSetLastKey(newRecentLastKey);//no await
 
-        if (this.recentLast !== oldRecentLast)
+        if (newRecentLastKey !== oldKey)
             this.emit('recent-changed');
 
         return result;
@@ -445,24 +503,12 @@ class BookManager {
                 delete mergedRecent[i];
         }
         
-        //"ленивое" обновление хранилища
-        (async() => {
-            for (const rec of Object.values(mergedRecent)) {
-                if (rec.key) {
-                    await bmRecentStore.setItem(rec.key, rec);
-                    await utils.sleep(1);
-                }
-            }
-        })();
-
         this.recent = mergedRecent;
 
-        this.recentLast = null;
-        await bmRecentStore.setItem('recent-last', this.recentLast);
+        await this.recentSetLastKey(null);
+        await this.recentSetItem(null, true);
 
-        this.recentChanged = true;
         this.emit('set-recent');
-        this.emit('recent-changed');
     }
 
     addEventListener(listener) {