👾 smth

This commit is contained in:
Lavrentiy Rubtsov 2022-05-15 18:35:59 +06:00 committed by GitHub
parent 2901e0c645
commit 9cceef1a7f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23

View file

@ -193,13 +193,12 @@ A key must be one of these types - number, date, string, binary, or array. It's
![](indexeddb-structure.svg) ![](indexeddb-structure.svg)
As we'll see very soon, we can provide a key when we add a value to the store, similar to `localStorage`. But when we store objects, IndexedDB allows setting up an object property as the key, which is much more convenient. Or we can auto-generate keys. As we'll see very soon, we can provide a key when we add a value to the store, similar to `localStorage`. But when we store objects, IndexedDB allows setting up an object property as the key, which is much more convenient. Or we can auto-generate keys.
But we need to create an object store first. But we need to create an object store first.
The syntax to create an object store: The syntax to create an object store:
```js ```js
db.createObjectStore(name[, keyOptions]); db.createObjectStore(name[, keyOptions]);
``` ```
@ -214,6 +213,7 @@ Please note, the operation is synchronous, no `await` needed.
If we don't supply `keyOptions`, then we'll need to provide a key explicitly later, when storing an object. If we don't supply `keyOptions`, then we'll need to provide a key explicitly later, when storing an object.
For instance, this object store uses `id` property as the key: For instance, this object store uses `id` property as the key:
```js ```js
db.createObjectStore('books', {keyPath: 'id'}); db.createObjectStore('books', {keyPath: 'id'});
``` ```
@ -223,6 +223,7 @@ db.createObjectStore('books', {keyPath: 'id'});
That's a technical limitation. Outside of the handler we'll be able to add/remove/update the data, but object stores can only be created/removed/altered during a version update. That's a technical limitation. Outside of the handler we'll be able to add/remove/update the data, but object stores can only be created/removed/altered during a version update.
To perform a database version upgrade, there are two main approaches: To perform a database version upgrade, there are two main approaches:
1. We can implement per-version upgrade functions: from 1 to 2, from 2 to 3, from 3 to 4 etc. Then, in `upgradeneeded` we can compare versions (e.g. old 2, now 4) and run per-version upgrades step by step, for every intermediate version (2 to 3, then 3 to 4). 1. We can implement per-version upgrade functions: from 1 to 2, from 2 to 3, from 3 to 4 etc. Then, in `upgradeneeded` we can compare versions (e.g. old 2, now 4) and run per-version upgrades step by step, for every intermediate version (2 to 3, then 3 to 4).
2. Or we can just examine the database: get a list of existing object stores as `db.objectStoreNames`. That object is a [DOMStringList](https://html.spec.whatwg.org/multipage/common-dom-interfaces.html#domstringlist) that provides `contains(name)` method to check for existance. And then we can do updates depending on what exists and what doesn't. 2. Or we can just examine the database: get a list of existing object stores as `db.objectStoreNames`. That object is a [DOMStringList](https://html.spec.whatwg.org/multipage/common-dom-interfaces.html#domstringlist) that provides `contains(name)` method to check for existance. And then we can do updates depending on what exists and what doesn't.
@ -242,7 +243,6 @@ openRequest.onupgradeneeded = function() {
}; };
``` ```
To delete an object store: To delete an object store:
```js ```js
@ -256,6 +256,7 @@ The term "transaction" is generic, used in many kinds of databases.
A transaction is a group of operations, that should either all succeed or all fail. A transaction is a group of operations, that should either all succeed or all fail.
For instance, when a person buys something, we need to: For instance, when a person buys something, we need to:
1. Subtract the money from their account. 1. Subtract the money from their account.
2. Add the item to their inventory. 2. Add the item to their inventory.
@ -614,6 +615,7 @@ The `delete` method looks up values to delete by a query, the call format is sim
- **`delete(query)`** -- delete matching values by query. - **`delete(query)`** -- delete matching values by query.
For instance: For instance:
```js ```js
// delete the book with id='js' // delete the book with id='js'
books.delete('js'); books.delete('js');
@ -632,6 +634,7 @@ request.onsuccess = function() {
``` ```
To delete everything: To delete everything:
```js ```js
books.clear(); // clear the storage. books.clear(); // clear the storage.
``` ```
@ -651,6 +654,7 @@ Cursors provide the means to work around that.
As an object store is sorted internally by key, a cursor walks the store in key order (ascending by default). As an object store is sorted internally by key, a cursor walks the store in key order (ascending by default).
The syntax: The syntax:
```js ```js
// like getAll, but with a cursor: // like getAll, but with a cursor:
let request = store.openCursor(query, [direction]); let request = store.openCursor(query, [direction]);
@ -748,7 +752,6 @@ try {
} catch(err) { } catch(err) {
console.log('error', err.message); console.log('error', err.message);
} }
``` ```
So we have all the sweet "plain async code" and "try..catch" stuff. So we have all the sweet "plain async code" and "try..catch" stuff.
@ -771,10 +774,8 @@ window.addEventListener('unhandledrejection', event => {
### "Inactive transaction" pitfall ### "Inactive transaction" pitfall
As we already know, a transaction auto-commits as soon as the browser is done with the current code and microtasks. So if we put a *macrotask* like `fetch` in the middle of a transaction, then the transaction won't wait for it to finish. It just auto-commits. So the next request in it would fail. As we already know, a transaction auto-commits as soon as the browser is done with the current code and microtasks. So if we put a *macrotask* like `fetch` in the middle of a transaction, then the transaction won't wait for it to finish. It just auto-commits. So the next request in it would fail.
For a promise wrapper and `async/await` the situation is the same. For a promise wrapper and `async/await` the situation is the same.
Here's an example of `fetch` in the middle of the transaction: Here's an example of `fetch` in the middle of the transaction:
@ -793,6 +794,7 @@ await inventory.add({ id: 'js', price: 10, created: new Date() }); // Error
The next `inventory.add` after `fetch` `(*)` fails with an "inactive transaction" error, because the transaction is already committed and closed at that time. The next `inventory.add` after `fetch` `(*)` fails with an "inactive transaction" error, because the transaction is already committed and closed at that time.
The workaround is the same as when working with native IndexedDB: either make a new transaction or just split things apart. The workaround is the same as when working with native IndexedDB: either make a new transaction or just split things apart.
1. Prepare the data and fetch all that's needed first. 1. Prepare the data and fetch all that's needed first.
2. Then save in the database. 2. Then save in the database.