Use Q promises and isPending to make logic simpler

This commit is contained in:
Kegan Dougal 2017-01-24 12:43:18 +00:00
parent f3df86872b
commit 6f3b70dbb0

View file

@ -16,6 +16,7 @@ limitations under the License.
import PlatformPeg from 'matrix-react-sdk/lib/PlatformPeg'; import PlatformPeg from 'matrix-react-sdk/lib/PlatformPeg';
import request from "browser-request"; import request from "browser-request";
import q from "q";
// This module contains all the code needed to log the console, persist it to // This module contains all the code needed to log the console, persist it to
// disk and submit bug reports. Rationale is as follows: // disk and submit bug reports. Rationale is as follows:
@ -105,9 +106,6 @@ class IndexedDBLogStore {
this.db = null; this.db = null;
// Promise is not null when a flush is IN PROGRESS // Promise is not null when a flush is IN PROGRESS
this.flushPromise = null; this.flushPromise = null;
// Promise is not null when flush() is called when one is already in
// progress.
this.flushAgainPromise = null;
} }
/** /**
@ -115,7 +113,7 @@ class IndexedDBLogStore {
*/ */
connect() { connect() {
let req = this.indexedDB.open("logs"); let req = this.indexedDB.open("logs");
return new Promise((resolve, reject) => { return q.Promise((resolve, reject) => {
req.onsuccess = (event) => { req.onsuccess = (event) => {
this.db = event.target.result; this.db = event.target.result;
// Periodically flush logs to local storage / indexeddb // Periodically flush logs to local storage / indexeddb
@ -167,13 +165,10 @@ class IndexedDBLogStore {
* - If B doesn't wait for A's flush to complete, B will be missing the * - If B doesn't wait for A's flush to complete, B will be missing the
* contents of A's flush. * contents of A's flush.
* To protect against this, we set 'flushPromise' when a flush is ongoing. * To protect against this, we set 'flushPromise' when a flush is ongoing.
* Subsequent calls to flush() during this period return a new promise * Subsequent calls to flush() during this period will chain another flush.
* 'flushAgainPromise' which is chained off the current 'flushPromise'. * This guarantees that we WILL do a brand new flush at some point in the
* Subsequent calls to flush() when the first flush hasn't completed will * future. Once the flushes have finished, it's safe to clobber the promise
* return the same 'flushAgainPromise' as we can guarantee that we WILL * with a new one to prevent very deep promise chains from building up.
* do a brand new flush at some point in the future. Once the first flush
* has completed, 'flushAgainPromise' becomes 'flushPromise' and can be
* chained again.
* *
* This guarantees that we will always eventually do a flush when flush() is * This guarantees that we will always eventually do a flush when flush() is
* called. * called.
@ -181,23 +176,20 @@ class IndexedDBLogStore {
* @return {Promise} Resolved when the logs have been flushed. * @return {Promise} Resolved when the logs have been flushed.
*/ */
flush() { flush() {
if (this.flushPromise) { // a flush is ongoing // check if a flush() operation is ongoing
if (this.flushAgainPromise) { // a flush is queued up, return that. if (this.flushPromise && this.flushPromise.isPending()) {
return this.flushAgainPromise; // chain a flush operation after this one has completed to guarantee
} // that a complete flush() is done. This does mean that if there are
// queue up a new flush // 3 calls to flush() in one go, the 2nd and 3rd promises will run
this.flushAgainPromise = this.flushPromise.then(() => { // concurrently, but this is fine since they can safely race when
// the current flush has completed, so shuffle the promises // collecting logs.
// around: return this.flushPromise.then(() => {
// flushAgainPromise => flushPromise and null flushAgainPromise.
// flushPromise has already nulled itself.
this.flushAgainPromise = null;
return this.flush(); return this.flush();
}); });
return this.flushAgainPromise;
} }
// there is no flush promise or there was but it has finished, so do
this.flushPromise = new Promise((resolve, reject) => { // a brand new one, destroying the chain which may have been built up.
this.flushPromise = q.Promise((resolve, reject) => {
if (!this.db) { if (!this.db) {
// not connected yet or user rejected access for us to r/w to // not connected yet or user rejected access for us to r/w to
// the db. // the db.
@ -225,10 +217,6 @@ class IndexedDBLogStore {
new Error("Failed to write logs: " + event.target.errorCode) new Error("Failed to write logs: " + event.target.errorCode)
); );
} }
}).then(() => {
this.flushPromise = null;
}, (err) => {
this.flushPromise = null;
}); });
return this.flushPromise; return this.flushPromise;
} }
@ -286,7 +274,7 @@ class IndexedDBLogStore {
} }
function deleteLogs(id) { function deleteLogs(id) {
return new Promise((resolve, reject) => { return q.Promise((resolve, reject) => {
const txn = db.transaction( const txn = db.transaction(
["logs", "logslastmod"], "readwrite" ["logs", "logslastmod"], "readwrite"
); );
@ -377,7 +365,7 @@ class IndexedDBLogStore {
*/ */
function selectQuery(store, keyRange, resultMapper) { function selectQuery(store, keyRange, resultMapper) {
const query = store.openCursor(keyRange); const query = store.openCursor(keyRange);
return new Promise((resolve, reject) => { return q.Promise((resolve, reject) => {
let results = []; let results = [];
query.onerror = (event) => { query.onerror = (event) => {
reject(new Error("Query failed: " + event.target.errorCode)); reject(new Error("Query failed: " + event.target.errorCode));
@ -479,7 +467,7 @@ module.exports = {
}); });
} }
await new Promise((resolve, reject) => { await q.Promise((resolve, reject) => {
request({ request({
method: "POST", method: "POST",
url: bugReportEndpoint, url: bugReportEndpoint,