Skip to content

Commit 8c5fa3a

Browse files
committed
minor fixes
1 parent 4a22c98 commit 8c5fa3a

File tree

1 file changed

+32
-23
lines changed

1 file changed

+32
-23
lines changed

6-data-storage/03-indexeddb/article.md

Lines changed: 32 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -5,22 +5,22 @@ libs:
55

66
# IndexedDB
77

8-
IndexedDB is a built-in database, much more powerful than `localStorage`.
8+
IndexedDB is a database, that is built into browser, much more powerful than `localStorage`.
99

10-
- Key/value storage: value can be (almost) anything, multiple key types.
10+
- Stores almost any kind of values by keys, multiple key types.
1111
- Supports transactions for reliability.
1212
- Supports key range queries, indexes.
13-
- Can store much more data than `localStorage`.
13+
- Can store much bigger volumes of data than `localStorage`.
1414

1515
That power is usually excessive for traditional client-server apps. IndexedDB is intended for offline apps, to be combined with ServiceWorkers and other technologies.
1616

1717
The native interface to IndexedDB, described in the specification <https://www.w3.org/TR/IndexedDB>, is event-based.
1818

19-
We can also use `async/await` with the help of a promise-based wrapper, like <https://github.com/jakearchibald/idb>. That's pretty convenient, but the wrapper is not perfect, it can't replace events for all cases. So we'll start with events, and then, after we gain understanding of IndexedDb, we'll use the wrapper.
19+
We can also use `async/await` with the help of a promise-based wrapper, like <https://github.com/jakearchibald/idb>. That's pretty convenient, but the wrapper is not perfect, it can't replace events for all cases. So we'll start with events, and then, after we gain understanding of IndexedDb, we'll use the wrapper.
2020

2121
## Open database
2222

23-
To start working with IndexedDB, we first need to open a database.
23+
To start working with IndexedDB, we first need to `open` (connect to) a database.
2424

2525
The syntax:
2626

@@ -33,20 +33,22 @@ let openRequest = indexedDB.open(name, version);
3333

3434
We can have many databases with different names, but all of them exist within the current origin (domain/protocol/port). Different websites can't access databases of each other.
3535

36-
After the call, we need to listen to events on `openRequest` object:
36+
The call returns `openRequest` object, we should listen to events on it:
3737
- `success`: database is ready, there's the "database object" in `openRequest.result`, that we should use it for further calls.
3838
- `error`: opening failed.
3939
- `upgradeneeded`: database is ready, but its version is outdated (see below).
4040

4141
**IndexedDB has a built-in mechanism of "schema versioning", absent in server-side databases.**
4242

43-
Unlike server-side databases, IndexedDB is client-side, the data is stored in the browser, so we, developers, don't have direct access to it. But when we publish a new version of our app, we may need to update the database.
43+
Unlike server-side databases, IndexedDB is client-side, the data is stored in the browser, so we, developers, don't have "any time" access to it. So, when we published a new version of our app, and the user visits our webpage, we may need to update the database.
4444

4545
If the local database version is less than specified in `open`, then a special event `upgradeneeded` is triggered, and we can compare versions and upgrade data structures as needed.
4646

47-
The event also triggers when the database did not exist yet, so we can perform initialization.
47+
The `upgradeneeded` event also triggers when the database did not exist yet (technically, it's version is `0`), so we can perform initialization.
4848

49-
When we first publish our app, we open it with version `1` and perform the initialization in `upgradeneeded` handler:
49+
Let's say we published the first version of our app.
50+
51+
Then we can open the database with version `1` and perform the initialization in `upgradeneeded` handler like this:
5052

5153
```js
5254
let openRequest = indexedDB.open("store", *!*1*/!*);
@@ -66,7 +68,9 @@ openRequest.onsuccess = function() {
6668
};
6769
```
6870

69-
When we publish the 2nd version:
71+
Then, later, we publish the 2nd version.
72+
73+
We can open it with version `2` and perform the upgrade like this:
7074

7175
```js
7276
let openRequest = indexedDB.open("store", *!*2*/!*);
@@ -85,9 +89,9 @@ openRequest.onupgradeneeded = function() {
8589
};
8690
```
8791

88-
So, in `openRequest.onupgradeneeded` we update the database. Soon we'll see how it's done. And then, only if its handler finishes without errors, `openRequest.onsuccess` triggers.
92+
Please note: as our current version is `2`, `onupgradeneeded` handler has a code branch for version `0`, suitable for users that come for the first time and have no database, and also for version `1`, for upgrades.
8993

90-
After `openRequest.onsuccess` we have the database object in `openRequest.result`, that we'll use for further operations.
94+
And then, only if `onupgradeneeded` handler finishes without errors, `openRequest.onsuccess` triggers, and the database is considered successfully opened.
9195

9296
To delete a database:
9397

@@ -96,29 +100,32 @@ let deleteRequest = indexedDB.deleteDatabase(name)
96100
// deleteRequest.onsuccess/onerror tracks the result
97101
```
98102

99-
```warn header="Can we open an old version?"
100-
Now what if we try to open a database with a lower version than the current one? E.g. the existing DB version is 3, and we try to `open(...2)`.
103+
```warn header="We can't open an older version of the database"
104+
If the current user database has a higher version than in the `open` call, e.g. the existing DB version is `3`, and we try to `open(...2)`, then that's an error, `openRequest.onerror` triggers.
101105
102-
That's an error, `openRequest.onerror` triggers.
106+
That's odd, but such thing may happen when a visitor loaded an outdated JavaScript code, e.g. from a proxy cache. So the code is old, but his database is new.
103107
104-
Such thing may happen if the visitor loaded an outdated code, e.g. from a proxy cache. We should check `db.version`, suggest him to reload the page. And also re-check our caching headers to ensure that the visitor never gets old code.
108+
To protect from errors, we should check `db.version` and suggest him to reload the page. Use proper HTTP caching headers to avoid loading the old code, so that you'll never have such problem.
105109
```
106110

107111
### Parallel update problem
108112

109113
As we're talking about versioning, let's tackle a small related problem.
110114

111-
Let's say, a visitor opened our site in a browser tab, with database version 1.
115+
Let's say:
116+
1. A visitor opened our site in a browser tab, with database version `1`.
117+
2. Then we rolled out an update, so our code is newer.
118+
3. And then the same visitor opens our site in another tab.
112119

113-
Then we rolled out an update, and the same visitor opens our site in another tab. So there are two tabs, both with our site, but one has an open connection with DB version 1, while the other one attempts to update it in `upgradeneeded` handler.
120+
So there's a tab with an open connection to DB version `1`, while the second tab one attempts to update it to version `2` in its `upgradeneeded` handler.
114121

115-
The problem is that a database is shared between two tabs, as that's the same site, same origin. And it can't be both version 1 and 2. To perform the update to version 2, all connections to version 1 must be closed.
122+
The problem is that a database is shared between two tabs, as it's the same site, same origin. And it can't be both version `1` and `2`. To perform the update to version `2`, all connections to version 1 must be closed, including the one in the first tab.
116123

117-
In order to organize that, the `versionchange` event triggers an open database object when a parallel upgrade is attempted. We should listen to it, so that we should close the database (and probably suggest the visitor to reload the page, to load the updated code).
124+
In order to organize that, the `versionchange` event triggers in such case on the "outdated" database object. We should listen to it and close the old database connection (and probably suggest the visitor to reload the page, to load the updated code).
118125

119-
If we don't close it, then the second, new connection will be blocked with `blocked` event instead of `success`.
126+
If we don't listen to `versionchange` event and don't close the old connection, then the second, new connection won't be made. The `openRequest` object will emit the `blocked` event instead of `success`. So the second tab won't work.
120127

121-
Here's the code to do that:
128+
Here's the code to correctly handle the parallel upgrade:
122129

123130
```js
124131
let openRequest = indexedDB.open("store", 2);
@@ -141,7 +148,9 @@ openRequest.onsuccess = function() {
141148

142149
*!*
143150
openRequest.onblocked = function() {
144-
// there's another open connection to same database
151+
// this event shouldn't trigger if we handle onversionchange correctly
152+
153+
// it means that there's another open connection to same database
145154
// and it wasn't closed after db.onversionchange triggered for them
146155
};
147156
*/!*

0 commit comments

Comments
 (0)