Add: improve import performance (#547)

* Add: implement db putMany

* Fix: putMany returns a list of ids
This commit is contained in:
eight 2018-11-11 14:04:22 +08:00 committed by GitHub
parent 8291da0bb5
commit 5646b8a0d9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 93 additions and 35 deletions

View File

@ -17,6 +17,7 @@ window.API_METHODS = Object.assign(window.API_METHODS || {}, {
getStyle: styleManager.get,
getStylesByUrl: styleManager.getStylesByUrl,
importStyle: styleManager.importStyle,
importManyStyles: styleManager.importMany,
installStyle: styleManager.installStyle,
styleExists: styleManager.styleExists,
toggleStyle: styleManager.toggleStyle,

View File

@ -74,39 +74,48 @@ const db = (() => {
}
function dbExecIndexedDB(method, ...args) {
return new Promise((resolve, reject) => {
Object.assign(indexedDB.open('stylish', 2), {
onsuccess(event) {
const database = event.target.result;
if (!method) {
resolve(database);
} else {
const transaction = database.transaction(['styles'], 'readwrite');
const store = transaction.objectStore('styles');
try {
Object.assign(store[method](...args), {
onsuccess: event => resolve(event, store, transaction, database),
onerror: reject,
});
} catch (err) {
reject(err);
}
}
},
onerror(event) {
console.warn(event.target.error || event.target.errorCode);
reject(event);
},
onupgradeneeded(event) {
return open().then(database => {
if (!method) {
return database;
}
if (method === 'putMany') {
return putMany(database, ...args);
}
const mode = method.startsWith('get') ? 'readonly' : 'readwrite';
const transaction = database.transaction(['styles'], mode);
const store = transaction.objectStore('styles');
return storeRequest(store, method, ...args);
});
function storeRequest(store, method, ...args) {
return new Promise((resolve, reject) => {
const request = store[method](...args);
request.onsuccess = resolve;
request.onerror = reject;
});
}
function open() {
return new Promise((resolve, reject) => {
const request = indexedDB.open('stylish', 2);
request.onsuccess = () => resolve(request.result);
request.onerror = reject;
request.onupgradeneeded = event => {
if (event.oldVersion === 0) {
event.target.result.createObjectStore('styles', {
keyPath: 'id',
autoIncrement: true,
});
}
},
};
});
});
}
function putMany(database, items) {
const transaction = database.transaction(['styles'], 'readwrite');
const store = transaction.objectStore('styles');
return Promise.all(items.map(item => storeRequest(store, 'put', item)));
}
}
function dbExecChromeStorage(method, data) {
@ -118,17 +127,33 @@ const db = (() => {
case 'put':
if (!data.id) {
return getAllStyles().then(styles => {
data.id = 1;
for (const style of styles) {
data.id = Math.max(data.id, style.id + 1);
}
return getMaxId().then(id => {
data.id = id + 1;
return dbExecChromeStorage('put', data);
});
}
return chromeLocal.setValue(STYLE_KEY_PREFIX + data.id, data)
.then(() => (chrome.runtime.lastError ? Promise.reject() : data.id));
case 'putMany': {
const newItems = data.filter(i => !i.id);
const doPut = () =>
chromeLocal.set(data.reduce((o, item) => {
o[STYLE_KEY_PREFIX + item.id] = item;
return o;
}, {}))
.then(() => data.map(d => ({target: {result: d.id}})));
if (newItems.length) {
return getMaxId().then(id => {
for (const item of newItems) {
item.id = ++id;
}
return doPut();
});
}
return doPut();
}
case 'delete':
return chromeLocal.remove(STYLE_KEY_PREFIX + data);
@ -150,5 +175,17 @@ const db = (() => {
return styles;
});
}
function getMaxId() {
return getAllStyles().then(styles => {
let result = 0;
for (const style of styles) {
if (style.id > result) {
result = style.id;
}
}
return result;
});
}
}
})();

View File

@ -55,6 +55,7 @@ const styleManager = (() => {
editSave,
findStyle,
importStyle,
importMany,
toggleStyle,
setStyleExclusions,
getAllStyles, // used by import-export
@ -138,6 +139,18 @@ const styleManager = (() => {
.then(newData => handleSave(newData, 'import'));
}
function importMany(items) {
return db.exec('putMany', items)
.then(events => {
for (let i = 0; i < items.length; i++) {
if (!items[i].id) {
items[i].id = events[i].target.result;
}
}
return Promise.all(items.map(i => handleSave(i, 'import')));
});
}
function installStyle(data, reason = null) {
const style = styles.get(data.id);
if (!style) {

View File

@ -126,13 +126,20 @@ function importFromString(jsonString) {
oldStyles.map(style => [style.id, style]));
oldStylesByName = json.length && new Map(
oldStyles.map(style => [style.name.trim(), style]));
return Promise.all(json.map((item, i) => {
const items = [];
json.forEach((item, i) => {
const info = analyze(item, i);
if (info) {
return API.importStyle(item)
.then(style => updateStats(style, info));
items.push({info, item});
}
}));
});
return API.importManyStyles(items.map(i => i.item))
.then(styles => {
for (let i = 0; i < styles.length; i++) {
updateStats(styles[i], items[i].info);
}
});
})
.then(done);