Swap to async/await rather than promise chains

Since we do in fact support coroutines!
This commit is contained in:
Kegan Dougal 2017-01-19 16:40:54 +00:00
parent 81d437ac1e
commit bf887e82fe
1 changed files with 65 additions and 96 deletions

View File

@ -166,7 +166,7 @@ class IndexedDBLogStore {
* when the log file was created (the log ID). The objects have said log ID in an "id" field and "lines" which is a * when the log file was created (the log ID). The objects have said log ID in an "id" field and "lines" which is a
* big string with all the new-line delimited logs. * big string with all the new-line delimited logs.
*/ */
consume(clearAll) { async consume(clearAll) {
const MAX_LOG_SIZE = 1024 * 1024 * 50; // 50 MB const MAX_LOG_SIZE = 1024 * 1024 * 50; // 50 MB
const db = this.db; const db = this.db;
@ -223,65 +223,36 @@ class IndexedDBLogStore {
}); });
} }
// Ideally we'd just use coroutines and a for loop but riot-web doesn't support async/await so instead let allLogIds = await fetchLogIds();
// recursively fetch logs up to the given threshold. We can't cheat and fetch all the logs let removeLogIds = [];
// from all time, but we may OOM if we do so. let logs = [];
// Returns: Promise<Object[]> : Each object having 'id' and 'lines'. Same ordering as logIds. let size = 0;
function fetchLogsToThreshold(logIds, threshold, logs) { for (let i = 0; i < allLogIds.length; i++) {
// Base case: check log size and return if bigger than threshold let lines = await fetchLogs(allLogIds[i]);
let size = 0; logs.push({
logs.forEach((l) => { lines: lines,
size += l.lines.length; id: allLogIds[i],
}); });
if (size > threshold) { size += lines.length;
return Promise.resolve(logs); if (size > MAX_LOG_SIZE) {
// the remaining log IDs should be removed. If we go out of bounds this is just []
removeLogIds = allLogIds.slice(i + 1);
break;
} }
// fetch logs for the first element
let logId = logIds.shift();
if (!logId) {
// no more entries
return Promise.resolve(logs);
}
return fetchLogs(logId).then((lines) => {
// add result to logs
logs.push({
lines: lines,
id: logId,
});
// recurse with the next log ID. TODO: Stack overflow risk?
return fetchLogsToThreshold(logIds, threshold, logs);
})
} }
if (clearAll) {
let allLogIds = []; removeLogIds = allLogIds;
return fetchLogIds().then((logIds) => { }
allLogIds = logIds.map((id) => id); // deep copy array as we'll modify it when fetching logs if (removeLogIds.length > 0) {
return fetchLogsToThreshold(logIds, MAX_LOG_SIZE, []); console.log("Removing logs: ", removeLogIds);
}).then((logs) => { // Don't await this because it's non-fatal if we can't clean up logs.
// Remove all logs that are beyond the threshold (not in logs), or the entire logs if clearAll was set. Promise.all(removeLogIds.map((id) => deleteLogs(id))).then(() => {
let removeLogIds = allLogIds; console.log(`Removed ${removeLogIds.length} old logs.`);
if (!clearAll) { }, (err) => {
removeLogIds = removeLogIds.filter((id) => { console.error(err);
for (let i = 0; i < logs.length; i++) { })
if (logs[i].id === id) { }console.log("async consumeeeee");
return false; // do not remove logs that we're about to return to the caller. return logs;
}
}
return true;
});
}
if (removeLogIds.length > 0) {
console.log("Removing logs: ", removeLogIds);
// Don't promise chain this because it's non-fatal if we can't clean up logs.
Promise.all(removeLogIds.map((id) => deleteLogs(id))).then(() => {
console.log(`Removed ${removeLogIds.length} old logs.`);
}, (err) => {
console.error(err);
})
}
return logs;
});
} }
_generateLogEntry(lines) { _generateLogEntry(lines) {
@ -350,22 +321,22 @@ module.exports = {
* Force-flush the logs to storage. * Force-flush the logs to storage.
* @return {Promise} Resolved when the logs have been flushed. * @return {Promise} Resolved when the logs have been flushed.
*/ */
flush: function() { flush: async function() {
if (!store) { if (!store) {
return Promise.resolve(); return;
} }
return store.flush(); await store.flush();
}, },
/** /**
* Clean up old logs. * Clean up old logs.
* @return Promise Resolves if cleaned logs. * @return Promise Resolves if cleaned logs.
*/ */
cleanup: function() { cleanup: async function() {
if (!store) { if (!store) {
return Promise.resolve(); return;
} }
return store.consume(false); await store.consume(false);
}, },
/** /**
@ -373,45 +344,43 @@ module.exports = {
* @param {string} userText Any additional user input. * @param {string} userText Any additional user input.
* @return {Promise} Resolved when the bug report is sent. * @return {Promise} Resolved when the bug report is sent.
*/ */
sendBugReport: function(userText) { sendBugReport: async function(userText) {
if (!logger) { if (!logger) {
return Promise.reject(new Error("No console logger, did you forget to call init()?")); throw new Error("No console logger, did you forget to call init()?");
} }
// If in incognito mode, store is null, but we still want bug report sending to work going off // If in incognito mode, store is null, but we still want bug report sending to work going off
// the in-memory console logs. // the in-memory console logs.
let promise = Promise.resolve([]); let logs = [];
if (store) { if (store) {
promise = store.consume(false); // TODO Swap to true to remove all logs logs = await store.consume(false);
} }
return promise.then((logs) => { // and add the most recent console logs which won't be in the store yet.
// and add the most recent console logs which won't be in the store yet. const consoleLogs = logger.flush(); // remove logs from console
const consoleLogs = logger.flush(); // remove logs from console const currentId = store ? store.id : "-";
const currentId = store ? store.id : "-"; logs.unshift({
logs.unshift({ lines: consoleLogs,
lines: consoleLogs, id: currentId,
id: currentId, });
}); await new Promise((resolve, reject) => {
return new Promise((resolve, reject) => { request({
request({ method: "POST",
method: "POST", url: "http://localhost:1337",
url: "http://localhost:1337", body: {
body: { logs: logs,
logs: logs, text: userText || "User did not supply any additional text.",
text: userText || "User did not supply any additional text.", },
}, json: true,
json: true, }, (err, res) => {
}, (err, res) => { if (err) {
if (err) { reject(err);
reject(err); return;
return; }
} if (res.status < 200 || res.status >= 400) {
if (res.status < 200 || res.status >= 400) { reject(new Error(`HTTP ${res.status}`));
reject(new Error(`HTTP ${res.status}`)); return;
return; }
} resolve();
resolve(); })
})
});
}); });
} }
}; };