track storage write requests internally, as we never await their promise

This commit is contained in:
Bruno Windels 2020-09-29 11:50:37 +02:00
parent 482b5f4d22
commit 37690cffe3
2 changed files with 40 additions and 28 deletions

View file

@ -106,8 +106,9 @@ class QueryTargetWrapper {
} }
export class Store extends QueryTarget { export class Store extends QueryTarget {
constructor(idbStore) { constructor(idbStore, transaction) {
super(new QueryTargetWrapper(idbStore)); super(new QueryTargetWrapper(idbStore));
this._transaction = transaction;
} }
get _idbStore() { get _idbStore() {
@ -118,33 +119,15 @@ export class Store extends QueryTarget {
return new QueryTarget(new QueryTargetWrapper(this._idbStore.index(indexName))); return new QueryTarget(new QueryTargetWrapper(this._idbStore.index(indexName)));
} }
async put(value) { put(value) {
try { this._transaction._addWriteRequest(this._idbStore.put(value));
return await reqAsPromise(this._idbStore.put(value));
} catch(err) {
const originalErr = err.cause;
throw new StorageError(`put on ${err.databaseName}.${err.storeName} failed`, originalErr, value);
}
} }
async add(value) { add(value) {
try { this._transaction._addWriteRequest(this._idbStore.add(value));
// this will catch both the sync error already mapped
// in the QueryTargetWrapper above, and also the async request errors, which are still DOMException's
return await reqAsPromise(this._idbStore.add(value));
} catch(err) {
const originalErr = err.cause;
throw new StorageError(`add on ${err.databaseName}.${err.storeName} failed`, originalErr, value);
}
} }
async delete(keyOrKeyRange) { delete(keyOrKeyRange) {
try { this._transaction._addWriteRequest(this._idbStore.delete(keyOrKeyRange));
return await reqAsPromise(this._idbStore.delete(keyOrKeyRange));
} catch(err) {
const originalErr = err.cause;
throw new StorageError(`delete on ${err.databaseName}.${err.storeName} failed`, originalErr, keyOrKeyRange);
}
} }
} }

View file

@ -14,6 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
import {IDBRequestError} from "./error.js";
import {txnAsPromise} from "./utils.js"; import {txnAsPromise} from "./utils.js";
import {StorageError} from "../common.js"; import {StorageError} from "../common.js";
import {Store} from "./Store.js"; import {Store} from "./Store.js";
@ -38,6 +39,14 @@ export class Transaction {
this._txn = txn; this._txn = txn;
this._allowedStoreNames = allowedStoreNames; this._allowedStoreNames = allowedStoreNames;
this._stores = {}; this._stores = {};
this._writeRequests = null;
}
_addWriteRequest(request) {
if (!this._writeRequests) {
this._writeRequests = [];
}
this._writeRequests.push(request);
} }
_idbStore(name) { _idbStore(name) {
@ -45,7 +54,7 @@ export class Transaction {
// more specific error? this is a bug, so maybe not ... // more specific error? this is a bug, so maybe not ...
throw new StorageError(`Invalid store for transaction: ${name}, only ${this._allowedStoreNames.join(", ")} are allowed.`); throw new StorageError(`Invalid store for transaction: ${name}, only ${this._allowedStoreNames.join(", ")} are allowed.`);
} }
return new Store(this._txn.objectStore(name)); return new Store(this._txn.objectStore(name), this);
} }
_store(name, mapStore) { _store(name, mapStore) {
@ -116,8 +125,28 @@ export class Transaction {
return this._store("accountData", idbStore => new AccountDataStore(idbStore)); return this._store("accountData", idbStore => new AccountDataStore(idbStore));
} }
complete() { async complete() {
return txnAsPromise(this._txn); // check the write requests if we haven't failed yet
if (this._writeRequests) {
for (const request of this._writeRequests) {
if (request.error) {
try {
this.abort();
} catch (err) {/* ignore abort error, although it would be useful to know if the other stuff got committed or not... */}
return Promise.reject(new IDBRequestError(request, "Write request failed"));
}
}
}
const result = await txnAsPromise(this._txn);
// check the write requests if we haven't failed yet
if (this._writeRequests) {
for (const request of this._writeRequests) {
if (request.readyState !== "done") {
return Promise.reject(new IDBRequestError(request, "Request is still pending after transaction finished"));
}
}
}
return result;
} }
abort() { abort() {