Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit fe15292

Browse files
author
root
committedJun 10, 2019
Merge branch 'master' of github.com:javascript-tutorial/en.javascript.info into sync-9cb33f40
2 parents 510dd09 + 9cb33f4 commit fe15292

File tree

14 files changed

+101
-100
lines changed

14 files changed

+101
-100
lines changed
 

‎1-js/02-first-steps/03-strict-mode/article.md

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,9 +53,17 @@ For the future, when you use a browser console to test features, please note tha
5353
5454
Sometimes, when `use strict` makes a difference, you'll get incorrect results.
5555
56-
Even if we press `key:Shift+Enter` to input multiple lines, and put `use strict` on top, it doesn't work. That's because of how the console executes the code internally.
56+
You can try to press `key:Shift+Enter` to input multiple lines, and put `use strict` on top, like this:
5757
58-
The reliable way to ensure `use strict` would be to input the code into console like this:
58+
```js
59+
'use strict'; <Shift+Enter for a newline>
60+
// ...your code
61+
<Enter to run>
62+
```
63+
64+
It works in most browsers, namely Firefox and Chrome.
65+
66+
If it doesn't, the most reliable way to ensure `use strict` would be to input the code into console like this:
5967

6068
```js
6169
(function() {

‎1-js/03-code-quality/04-ninja-code/article.md

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,7 @@ Instead, reuse existing names. Just write new values into them.
137137

138138
In a function try to use only variables passed as parameters.
139139

140-
That would make it really hard to identify what's exactly in the variable *now*. And also where it comes from. A person with weak intuition would have to analyze the code line-by-line and track the changes through every code branch.
140+
That would make it really hard to identify what's exactly in the variable *now*. And also where it comes from. The purpose is to develop the intuition and memory of a person reading the code. A person with weak intuition would have to analyze the code line-by-line and track the changes through every code branch.
141141

142142
**An advanced variant of the approach is to covertly (!) replace the value with something alike in the middle of a loop or a function.**
143143

@@ -155,7 +155,7 @@ function ninjaFunction(elem) {
155155

156156
A fellow programmer who wants to work with `elem` in the second half of the function will be surprised... Only during the debugging, after examining the code they will find out that they're working with a clone!
157157

158-
Seen in code regularly. Deadly effective even against an experienced ninja.
158+
Seen in code regularly. Deadly effective even against an experienced ninja.
159159

160160
## Underscores for fun
161161

@@ -169,8 +169,7 @@ A smart ninja puts underscores at one spot of code and evades them at other plac
169169

170170
Let everyone see how magnificent your entities are! Names like `superElement`, `megaFrame` and `niceItem` will definitely enlighten a reader.
171171

172-
Indeed, from one hand, something is written: `super..`, `mega..`, `nice..` But from the other hand -- that brings no details. A reader may decide to look for a hidden meaning and meditate for an hour or two.
173-
172+
Indeed, from one hand, something is written: `super..`, `mega..`, `nice..` But from the other hand -- that brings no details. A reader may decide to look for a hidden meaning and meditate for an hour or two of their paid working time.
174173

175174

176175
## Overlap outer variables
@@ -180,7 +179,7 @@ When in the light, can't see anything in the darkness.<br>
180179
When in the darkness, can see everything in the light.
181180
```
182181

183-
Use same names for variables inside and outside a function. As simple. No efforts required.
182+
Use same names for variables inside and outside a function. As simple. No efforts to invent new names.
184183

185184
```js
186185
let *!*user*/!* = authenticateUser();

‎1-js/06-advanced-functions/11-currying-partials/article.md

Lines changed: 10 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -170,7 +170,7 @@ To understand the benefits we definitely need a worthy real-life example.
170170

171171
Advanced currying allows the function to be both callable normally and partially.
172172

173-
For instance, we have the logging function `log(date, importance, message)` that formats and outputs the information. In real projects such functions also have many other useful features like sending logs over the network:
173+
For instance, we have the logging function `log(date, importance, message)` that formats and outputs the information. In real projects such functions also have many other useful features like sending logs over the network, here we just use `alert`:
174174

175175
```js
176176
function log(date, importance, message) {
@@ -184,34 +184,29 @@ Let's curry it!
184184
log = _.curry(log);
185185
```
186186

187-
After that `log` still works the normal way:
188-
189-
```js
190-
log(new Date(), "DEBUG", "some debug");
191-
```
192-
193-
...But also can be called in the curried form:
187+
After that `log` work both the normal way and in the curried form:
194188

195189
```js
190+
log(new Date(), "DEBUG", "some debug"); // log(a,b,c)
196191
log(new Date())("DEBUG")("some debug"); // log(a)(b)(c)
197192
```
198193

199-
Let's get a convenience function for today's logs:
194+
Now we can easily make a convenience function for current logs:
200195

201196
```js
202-
// todayLog will be the partial of log with fixed first argument
203-
let todayLog = log(new Date());
197+
// currentLog will be the partial of log with fixed first argument
198+
let currentLog = log(new Date());
204199

205200
// use it
206-
todayLog("INFO", "message"); // [HH:mm] INFO message
201+
currentLog("INFO", "message"); // [HH:mm] INFO message
207202
```
208203

209-
And now a convenience function for today's debug messages:
204+
And here's a convenience function for current debug messages:
210205

211206
```js
212-
let todayDebug = todayLog("DEBUG");
207+
let todayDebug = currentLog("DEBUG");
213208

214-
todayDebug("message"); // [HH:mm] DEBUG message
209+
currentLog("message"); // [HH:mm] DEBUG message
215210
```
216211

217212
So:

‎1-js/11-async/02-promise-basics/article.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -278,7 +278,7 @@ The good thing is: a `.then` handler is guaranteed to run whether the promise ta
278278

279279
Next, let's see more practical examples of how promises can help us to write asynchronous code.
280280

281-
## Example: loadScript
281+
## Example: loadScript [#loadscript]
282282

283283
We've got the `loadScript` function for loading a script from the previous chapter.
284284

‎1-js/11-async/03-promise-chaining/article.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -146,7 +146,7 @@ Returning promises allows us to build chains of asynchronous actions.
146146

147147
## Example: loadScript
148148

149-
Let's use this feature with `loadScript` to load scripts one by one, in sequence:
149+
Let's use this feature with the promisified `loadScript`, defined in the [previous chapter](/promise-basics#loadscript), to load scripts one by one, in sequence:
150150

151151
```js run
152152
loadScript("/article/promise-chaining/one.js")
@@ -305,7 +305,7 @@ fetch('/article/promise-chaining/user.json')
305305
});
306306
```
307307

308-
The code works, see comments about the details, but it should be quite self-descriptive. Although, there's a potential problem in it, a typical error of those who begin to use promises.
308+
The code works, see comments about the details. Although, there's a potential problem in it, a typical error of those who begin to use promises.
309309

310310
Look at the line `(*)`: how can we do something *after* the avatar has finished showing and gets removed? For instance, we'd like to show a form for editing that user or something else. As of now, there's no way.
311311

‎1-js/11-async/07-microtask-queue/article.md

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,6 @@ Naturally, `promise` shows up first, because `setTimeout` macrotask awaits in th
124124

125125
As a logical consequence, macrotasks are handled only when promises give the engine a "free time". So if we have a chain of promise handlers that don't wait for anything, execute right one after another, then a `setTimeout` (or a user action handler) can never run in-between them.
126126

127-
128127
## Unhandled rejection
129128

130129
Remember "unhandled rejection" event from the chapter <info:promise-error-handling>?

‎1-js/12-generators-iterators/2-async-iterators-generators/article.md

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,13 @@
33

44
Asynchronous iterators allow to iterate over data that comes asynchronously, on-demand.
55

6-
For instance, when we download something chunk-by-chunk, or just expect events to come asynchronously and would like to iterate over them -- async iterators and generators may come in handy. Let's see a simple example first, to grasp the syntax, and then review a real-life use case.
6+
For instance, when we download something chunk-by-chunk, and expect data fragments to come asynchronously and would like to iterate over them -- async iterators and generators may come in handy. Let's see a simple example first, to grasp the syntax, and then review a real-life use case.
77

88
## Async iterators
99

10-
Asynchronous iterators are totally similar to regular iterators, with a few syntactic differences.
10+
Asynchronous iterators are similar to regular iterators, with a few syntactic differences.
1111

12-
"Regular" iterable object from the chapter <info:iterable> look like this:
12+
"Regular" iterable object, as described in the chapter <info:iterable>, look like this:
1313

1414
```js run
1515
let range = {
@@ -101,7 +101,7 @@ let range = {
101101
})()
102102
```
103103

104-
As we can see, the components are similar to regular iterators:
104+
As we can see, the structure is similar to regular iterators:
105105

106106
1. To make an object asynchronously iterable, it must have a method `Symbol.asyncIterator` `(1)`.
107107
2. It must return the object with `next()` method returning a promise `(2)`.
@@ -117,22 +117,22 @@ Here's a small cheatsheet:
117117
| to loop, use | `for..of` | `for await..of` |
118118

119119

120-
````warn header="The spread operator doesn't work asynchronously"
120+
````warn header="The spread operator ... doesn't work asynchronously"
121121
Features that require regular, synchronous iterators, don't work with asynchronous ones.
122122
123123
For instance, a spread operator won't work:
124124
```js
125125
alert( [...range] ); // Error, no Symbol.iterator
126126
```
127127
128-
That's natural, as it expects to find `Symbol.iterator`, same as `for..of` without `await`.
128+
That's natural, as it expects to find `Symbol.iterator`, same as `for..of` without `await`. Not `Symbol.asyncIterator`.
129129
````
130130

131131
## Async generators
132132

133-
JavaScript also provides generators, that are also iterable.
133+
As we already know, JavaScript also supprots generators, and they are iterable.
134134

135-
Let's recall a sequence generator from the chapter [](info:generators). It generates a sequence of values from `start` to `end` (could be anything else):
135+
Let's recall a sequence generator from the chapter [](info:generators). It generates a sequence of values from `start` to `end`:
136136

137137
```js run
138138
function* generateSequence(start, end) {
@@ -147,7 +147,7 @@ for(let value of generateSequence(1, 5)) {
147147
```
148148

149149

150-
Normally, we can't use `await` in generators. All values must come synchronously: there's no place for delay in `for..of`.
150+
Normally, we can't use `await` in generators. All values must come synchronously: there's no place for delay in `for..of`, it's a synchronous construct.
151151

152152
But what if we need to use `await` in the generator body? To perform network requests, for instance.
153153

@@ -184,15 +184,15 @@ It's indeed very simple. We add the `async` keyword, and the generator now can u
184184

185185
Technically, another the difference of an async generator is that its `generator.next()` method is now asynchronous also, it returns promises.
186186

187-
Instead of `result = generator.next()` for a regular, non-async generator, values can be obtained like this:
187+
In a regular generator we'd use `result = generator.next()` to get values. In an async generator, we should add `await`, like this:
188188

189189
```js
190190
result = await generator.next(); // result = {value: ..., done: true/false}
191191
```
192192

193193
## Iterables via async generators
194194

195-
When we'd like to make an object iterable, we should add `Symbol.iterator` to it.
195+
As we already know, to make an object iterable, we should add `Symbol.iterator` to it.
196196

197197
```js
198198
let range = {
@@ -270,7 +270,7 @@ The pattern is very common, it's not about users, but just about anything. For i
270270
- It responds with a JSON of 30 commits, and also provides a link to the next page in the `Link` header.
271271
- Then we can use that link for the next request, to get more commits, and so on.
272272

273-
What we'd like to have is an iterable source of commits, so that we could use it like this:
273+
What we'd like to have is a simpler API: an iterable object with commits, so that we could go over them like this:
274274

275275
```js
276276
let repo = 'javascript-tutorial/en.javascript.info'; // GitHub repository to get commits from
@@ -332,7 +332,7 @@ An example of use (shows commit authors in console):
332332
})();
333333
```
334334

335-
That's just what we wanted. The internal pagination mechanics is invisible from the outside. For us it's just an async generator that returns commits.
335+
That's just what we wanted. The internal mechanics of paginated requests is invisible from the outside. For us it's just an async generator that returns commits.
336336

337337
## Summary
338338

@@ -356,6 +356,6 @@ Syntax differences between async and regular generators:
356356

357357
In web-development we often meet streams of data, when it flows chunk-by-chunk. For instance, downloading or uploading a big file.
358358

359-
We could use async generators to process such data, but there's also another API called Streams, that may be more convenient, as it provides special interfaces to transform the data and to pass it from one stream to another (e.g. download from one place and immediately send elsewhere). But they are also more complex.
359+
We can use async generators to process such data, but it's worth to mention that there's also another API called Streams, that provides special interfaces to transform the data and to pass it from one stream to another (e.g. download from one place and immediately send elsewhere).
360360

361361
Streams API not a part of JavaScript language standard. Streams and async generators complement each other, both are great ways to handle async data flows.

‎2-ui/1-document/08-styles-and-classes/article.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -66,10 +66,10 @@ So we can operate both on the full class string using `className` or on individu
6666
Methods of `classList`:
6767

6868
- `elem.classList.add/remove("class")` -- adds/removes the class.
69-
- `elem.classList.toggle("class")` -- if the class exists, then removes it, otherwise adds it.
69+
- `elem.classList.toggle("class")` -- adds the class if it doesn't exist, otherwise removes it.
7070
- `elem.classList.contains("class")` -- returns `true/false`, checks for the given class.
7171

72-
Besides that, `classList` is iterable, so we can list all classes like this:
72+
Besides, `classList` is iterable, so we can list all classes with `for..of`, like this:
7373

7474
```html run
7575
<body class="main page">
@@ -147,7 +147,7 @@ To set the full style as a string, there's a special property `style.cssText`:
147147
</script>
148148
```
149149

150-
We rarely use it, because such assignment removes all existing styles: it does not add, but replaces them. May occasionally delete something needed. But still can be done for new elements when we know we won't delete something important.
150+
This property is rarely used, because such assignment removes all existing styles: it does not add, but replaces them. May occasionally delete something needed. But we can safely use it for new elements, when we know we won't delete an existing style.
151151

152152
The same can be accomplished by setting an attribute: `div.setAttribute('style', 'color: red...')`.
153153
````

‎2-ui/2-events/03-event-delegation/4-behavior-tooltip/task.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ Should work like this:
1818

1919
[iframe src="solution" height=200 border=1]
2020

21-
In this task we assume that all elements with `data-tooltip` have only text inside. No nested tags.
21+
In this task we assume that all elements with `data-tooltip` have only text inside. No nested tags (yet).
2222

2323
Details:
2424

@@ -33,4 +33,4 @@ Please use event delegation: set up two handlers on `document` to track all "ove
3333

3434
After the behavior is implemented, even people unfamiliar with JavaScript can add annotated elements.
3535

36-
P.S. To keep things natural and simple: only one tooltip may show up at a time.
36+
P.S. Only one tooltip may show up at a time.

‎2-ui/2-events/03-event-delegation/article.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -182,7 +182,7 @@ The pattern has two parts:
182182

183183
### Counter
184184

185-
For instance, here the attribute `data-counter` adds a behavior: "increase on click" to buttons:
185+
For instance, here the attribute `data-counter` adds a behavior: "increase value on click" to buttons:
186186

187187
```html run autorun height=60
188188
Counter: <input type="button" value="1" data-counter>

‎2-ui/5-loading/02-script-async-defer/article.md

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -188,8 +188,11 @@ But there are also essential differences between them:
188188
| `defer` | *Document order* (as they go in the document). | Execute after the document is loaded and parsed (they wait if needed), right before `DOMContentLoaded`. |
189189

190190
```warn header="Page without scripts should be usable"
191-
Please note that if you're using `defer`, then the page is visible before the script loads and enables all the graphical components.
191+
Please note that if you're using `defer`, then the page is visible *before* the script loads.
192192
193-
So, buttons should be disabled by CSS or by other means, to let the user
193+
So the user may read the page, but some graphical components are probably not ready yet.
194+
195+
There should be "loading" indication in proper places, not-working buttons disabled, to clearly show the user what's ready and what's not.
196+
```
194197

195198
In practice, `defer` is used for scripts that need the whole DOM and/or their relative execution order is important. And `async` is used for independent scripts, like counters or ads. And their relative execution order does not matter.

‎6-data-storage/02-localstorage/article.md

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -42,9 +42,9 @@ localStorage.setItem('test', 1);
4242
alert( localStorage.getItem('test') ); // 1
4343
```
4444

45-
We only have to be on the same domain/port/protocol, the url path can be different.
45+
We only have to be on the same origin (domain/port/protocol), the url path can be different.
4646

47-
The `localStorage` is shared, so if we set the data in one window, the change becomes visible in the other one.
47+
The `localStorage` is shared between all windows with the same origin, so if we set the data in one window, the change becomes visible in another one.
4848

4949
## Object-like access
5050

@@ -73,7 +73,7 @@ That's allowed for historical reasons, and mostly works, but generally not recom
7373

7474
## Looping over keys
7575

76-
As we've seen, the methods provide get/set/remove functionality. But how to get all saved values or keys?
76+
As we've seen, the methods provide "get/set/remove by key" functionality. But how to get all saved values or keys?
7777
7878
Unfortunately, storage objects are not iterable.
7979
@@ -198,7 +198,7 @@ Imagine, you have two windows with the same site in each. So `localStorage` is s
198198
You might want to open this page in two browser windows to test the code below.
199199
```
200200

201-
Now if both windows are listening for `window.onstorage`, then each one will react on updates that happened in the other one.
201+
If both windows are listening for `window.onstorage`, then each one will react on updates that happened in the other one.
202202

203203
```js run
204204
// triggers on updates made to the same storage from other documents
@@ -229,18 +229,18 @@ Web storage objects `localStorage` and `sessionStorage` allow to store key/value
229229
| `localStorage` | `sessionStorage` |
230230
|----------------|------------------|
231231
| Shared between all tabs and windows with the same origin | Visible within a browser tab, including iframes from the same origin |
232-
| Survives browser restart | Dies on tab close |
232+
| Survives browser restart | Survives page refresh (but not tab close) |
233233

234234
API:
235235

236236
- `setItem(key, value)` -- store key/value pair.
237237
- `getItem(key)` -- get the value by key.
238238
- `removeItem(key)` -- remove the key with its value.
239239
- `clear()` -- delete everything.
240-
- `key(index)` -- get the key on a given position.
240+
- `key(index)` -- get the key number `index`.
241241
- `length` -- the number of stored items.
242242
- Use `Object.keys` to get all keys.
243-
- Can use the keys as object properties, in that case `storage` event isn't triggered.
243+
- We access keys as object properties, in that case `storage` event isn't triggered.
244244
245245
Storage event:
246246

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

Lines changed: 39 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,11 @@ That power is usually excessive for traditional client-server apps. IndexedDB is
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 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 need to open a database.
23+
To start working with IndexedDB, we first need to open a database.
2424

2525
The syntax:
2626

@@ -31,16 +31,16 @@ let openRequest = indexedDB.open(name, version);
3131
- `name` -- a string, the database name.
3232
- `version` -- a positive integer version, by default `1` (explained below).
3333

34-
We can have many databases with different names, all within the current origin (domain/protocol/port). So different websites can't access databases of each other.
34+
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

3636
After the call, we need to listen to events on `openRequest` object:
37-
- `success`: database is ready, use the database object `openRequest.result` for further work.
37+
- `success`: database is ready, there's the "database object" in `openRequest.result`, that we should use it for further calls.
3838
- `error`: open failed.
3939
- `upgradeneeded`: database 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, we don't have the data at hands. 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, in the browser, so we don't have the data at hands. But when we publish a new version of our app, 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

@@ -109,11 +109,13 @@ An example of object that can't be stored: an object with circular references. S
109109

110110
**There must be an unique `key` for every value in the store.**
111111

112-
A key must have a type one of: number, date, string, binary, or array. It's a unique object identifier: we can search/remove/update values by the key.
112+
A key must have a type one of: number, date, string, binary, or array. It's an unique identifier: we can search/remove/update values by the key.
113113

114114
![](indexeddb-structure.png)
115115

116-
We can provide a key when we add an value to the store, similar to `localStorage`. That's good for storing primitive values. But when we store objects, IndexedDB allows to setup an object property as the key, that's much more convenient. Or we can auto-generate keys.
116+
As we'll see very soon, we can provide a key when we add an value to the store, similar to `localStorage`. But when we store objects, IndexedDB allows to setup an object property as the key, that's much more convenient. Or we can auto-generate keys.
117+
118+
But we need to create an object store first.
117119

118120
The syntax to create an object store:
119121
```js
@@ -127,7 +129,7 @@ Please note, the operation is synchronous, no `await` needed.
127129
- `keyPath` -- a path to an object property that IndexedDB will use as the key, e.g. `id`.
128130
- `autoIncrement` -- if `true`, then the key for a newly stored object is generated automatically, as an ever-incrementing number.
129131

130-
If we don't supply any options, then we'll need to provide a key explicitly later, when storing an object.
132+
If we don't supply `keyOptions`, then we'll need to provide a key explicitly later, when storing an object.
131133

132134
For instance, this object store uses `id` property as the key:
133135
```js
@@ -136,22 +138,24 @@ db.createObjectStore('books', {keyPath: 'id'});
136138

137139
**An object store can only be created/modified while updating the DB version, in `upgradeneeded` handler.**
138140

139-
That's a technical limitation. Outside of the handler we'll be able to add/remove/update the data, but object stores are changed only during version update.
141+
That's a technical limitation. Outside of the handler we'll be able to add/remove/update the data, but object stores can be created/removed/altered only during version update.
142+
143+
To perform database version upgrade, there are two main approaches:
144+
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).
145+
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.
140146

141-
To do an upgrade, there are two main ways:
142-
1. We can compare versions and run per-version operations.
143-
2. Or we can 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), and it provides `contains(name)` method to check for the existance. And then we can do updates depending on what exists.
147+
For small databases the second path may be simpler.
144148

145149
Here's the demo of the second approach:
146150

147151
```js
148152
let openRequest = indexedDB.open("db", 1);
149153

150-
// create an object store for books if not exists
154+
// create/upgrade the database without version checks
151155
openRequest.onupgradeneeded = function() {
152156
let db = openRequest.result;
153-
if (!db.objectStoreNames.contains('books')) {
154-
db.createObjectStore('books', {keyPath: 'id'});
157+
if (!db.objectStoreNames.contains('books')) { // if there's no "books" store
158+
db.createObjectStore('books', {keyPath: 'id'}); // create it
155159
}
156160
};
157161
```
@@ -188,14 +192,14 @@ db.transaction(store[, type]);
188192
- `store` is a store name that the transaction is going to access, e.g. `"books"`. Can be an array of store names if we're going to access multiple stores.
189193
- `type` – a transaction type, one of:
190194
- `readonly` -- can only read, the default.
191-
- `readwrite` -- can only read and write, but not modify object stores.
195+
- `readwrite` -- can only read and write the data, but not create/remove/alter object stores.
192196

193197
There'is also `versionchange` transaction type: such transactions can do everything, but we can't create them manually. IndexedDB automatically creates a `versionchange` transaction when opening the database, for `updateneeded` handler. That's why it's a single place where we can update the database structure, create/remove object stores.
194198

195-
```smart header="What are transaction types for?"
199+
```smart header="Why there exist different types of transactions?"
196200
Performance is the reason why transactions need to be labeled either `readonly` and `readwrite`.
197201
198-
Many `readonly` transactions can access concurrently the same store, but `readwrite` transactions can't. A `readwrite` transaction "locks" the store for writing. The next transaction must wait before the previous one finishes before accessing the same store.
202+
Many `readonly` transactions are able to access concurrently the same store, but `readwrite` transactions can't. A `readwrite` transaction "locks" the store for writing. The next transaction must wait before the previous one finishes before accessing the same store.
199203
```
200204

201205
After the transaction is created, we can add an item to the store, like this:
@@ -227,12 +231,12 @@ request.onerror = function() {
227231
};
228232
```
229233

230-
There are basically four steps:
234+
There were basically four steps:
231235

232236
1. Create a transaction, mention all stores it's going to access, at `(1)`.
233237
2. Get the store object using `transaction.objectStore(name)`, at `(2)`.
234238
3. Perform the request to the object store `books.add(book)`, at `(3)`.
235-
4. ...Handle request success/error `(4)`, make other requests if needed, etc.
239+
4. ...Handle request success/error `(4)`, then we can make other requests if needed, etc.
236240

237241
Object stores support two methods to store a value:
238242

@@ -242,30 +246,24 @@ Object stores support two methods to store a value:
242246
- **add(value, [key])**
243247
Same as `put`, but if there's already a value with the same key, then the request fails, and an error with the name `"ConstraintError"` is generated.
244248

245-
Just like when opening a database, we send a request: `books.add(book)`, and then wait for `success/error` events.
249+
Similar to opening a database, we can send a request: `books.add(book)`, and then wait for `success/error` events.
246250

247251
- The `request.result` for `add` is the key of the new object.
248252
- The error is in `request.error` (if any).
249253

250-
## Transactions autocommit
254+
## Transactions' autocommit
251255

252-
In the example above we started the transaction and made `add` request. We could make more requests. How do we finish ("commit") the transaction?
256+
In the example above we started the transaction and made `add` request. But as we stated previously, a transaction may have multiple associated requests, that must either all success or all fail. How do we mark the transaction as finished, no more requests to come?
253257

254258
The short answer is: we don't.
255259

256260
In the next version 3.0 of the specification, there will probably be a manual way to finish the transaction, but right now in 2.0 there isn't.
257261

258262
**When all transaction requests are finished, and the [microtasks queue](info:microtask-queue) is empty, it is committed automatically.**
259263

260-
```smart header="What's an \"empty microtask queue\"?"
261-
The microtask queue is explained in [another chapter](info:async-await#microtask-queue). In short, an empty microtask queue means that for all settled promises their `.then/catch/finally` handlers are executed.
264+
Usually, we can assume that a transaction commits when all its requests are complete, and the current code finishes.
262265

263-
In other words, handling of finished promises and resuming "awaits" is done before closing the transaction.
264-
265-
That's a minor technical detail. If we're using `async/await` instead of low-level promise calls, then we can assume that a transaction commits when all its requests are done, and the current code finishes.
266-
```
267-
268-
So, in the example above no special code is needed to finish the transaction.
266+
So, in the example above no special call is needed to finish the transaction.
269267

270268
Transactions auto-commit principle has an important side effect. We can't insert an async operation like `fetch`, `setTimeout` in the middle of transaction. IndexedDB will not keep the transaction waiting till these are done.
271269

@@ -331,9 +329,9 @@ That's to be expected, not only because of possible errors at our side, but also
331329

332330
**A failed request automatically aborts the transaction, canceling all its changes.**
333331

334-
Sometimes a request may fail with a non-critical error. We'd like to handle it in `request.onerror` and continue the transaction. Then, to prevent the transaction abort, we should call `event.preventDefault()`.
332+
In some situations, we may want to handle the failure (e.g. try another request), without canceling existing changes, and continue the transaction. That's possible. The `request.onerror` handler is able to prevent the transaction abort by calling `event.preventDefault()`.
335333

336-
In the example below a new book is added with the same key (`id`). The `store.add` method generates a `"ConstraintError"` in that case. We handle it without canceling the transaction:
334+
In the example below a new book is added with the same key (`id`) as the existing one. The `store.add` method generates a `"ConstraintError"` in that case. We handle it without canceling the transaction:
337335

338336
```js
339337
let transaction = db.transaction("books", "readwrite");
@@ -347,6 +345,7 @@ request.onerror = function(event) {
347345
if (request.error.name == "ConstraintError") {
348346
console.log("Book with such id already exists"); // handle the error
349347
event.preventDefault(); // don't abort the transaction
348+
// use another key for the book?
350349
} else {
351350
// unexpected error, can't handle it
352351
// the transaction will abort
@@ -396,9 +395,9 @@ request.onerror = function(event) {
396395

397396
## Searching by keys
398397

399-
There are two main ways to search in an object store:
398+
There are two main types of search in an object store:
400399
1. By a key or a key range. That is: by `book.id` in our "books" storage.
401-
2. By another object field, e.g. `book.price`. We need an index for that.
400+
2. By another object field, e.g. `book.price`.
402401

403402
First let's deal with the keys and key ranges `(1)`.
404403

@@ -524,7 +523,7 @@ Indexes are internally sorted by the tracked object field, `price` in our case.
524523

525524
## Deleting from store
526525

527-
The `delete` method looks up values to delete by a query, just like `getAll`.
526+
The `delete` method looks up values to delete by a query, the call format is similar to `getAll`:
528527

529528
- **`delete(query)`** -- delete matching values by query.
530529

@@ -555,9 +554,7 @@ books.clear(); // clear the storage.
555554

556555
Methods like `getAll/getAllKeys` return an array of keys/values.
557556

558-
But an object storage can be huge, bigger than the available memory.
559-
560-
Then `getAll` will fail to get all records as an array.
557+
But an object storage can be huge, bigger than the available memory. Then `getAll` will fail to get all records as an array.
561558

562559
What to do?
563560

@@ -608,7 +605,7 @@ request.onsuccess = function() {
608605
The main cursor methods are:
609606

610607
- `advance(count)` -- advance the cursor `count` times, skipping values.
611-
- `continue([key])` -- advance the cursor to the next value in range matching or after key.
608+
- `continue([key])` -- advance the cursor to the next value in range matching (or immediately after `key` if given).
612609

613610
Whether there are more values matching the cursor or not -- `onsuccess` gets called, and then in `result` we can get the cursor pointing to the next record, or `undefined`.
614611

@@ -672,7 +669,7 @@ So we have all the sweet "plain async code" and "try..catch" stuff.
672669

673670
### Error handling
674671

675-
If we don't catch the error, then it falls through, just as usual.
672+
If we don't catch an error, then it falls through, till the closest outer `try..catch`.
676673

677674
An uncaught error becomes an "unhandled promise rejection" event on `window` object.
678675

@@ -688,7 +685,7 @@ window.addEventListener('unhandledrejection', event => {
688685

689686
### "Inactive transaction" pitfall
690687

691-
A we know already, a transaction auto-commits as soon as the browser is done with the current code and microtasks. So if we put an *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 fails.
688+
A we know already, a transaction auto-commits as soon as the browser is done with the current code and microtasks. So if we put an *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.
692689

693690
For a promise wrapper and `async/await` the situation is the same.
694691

‎9-regular-expressions/10-regexp-backreferences/article.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# Backreferences in pattern: \n and \k
22

3-
Capturing groups can be accessed not only in the result or in the replacement string, but also in the pattern itself.
3+
We can use the contents of capturing groups `(...)` not only in the result or in the replacement string, but also in the pattern itself.
44

55
## Backreference by number: \n
66

@@ -12,7 +12,7 @@ We need to find a quoted string: either a single-quoted `subject:'...'` or a dou
1212

1313
How to look for them?
1414

15-
We can put two kinds of quotes in the pattern: `pattern:['"](.*?)['"]`, but it would find strings with mixed quotes, like `match:"...'` and `match:'..."`. That would lead to incorrect matches when one quote appears inside other ones, like the string `subject:"She's the one!"`:
15+
We can put both kinds of quotes in the square brackets: `pattern:['"](.*?)['"]`, but it would find strings with mixed quotes, like `match:"...'` and `match:'..."`. That would lead to incorrect matches when one quote appears inside other ones, like the string `subject:"She's the one!"`:
1616

1717
```js run
1818
let str = `He said: "She's the one!".`;
@@ -25,7 +25,7 @@ alert( str.match(reg) ); // "She'
2525

2626
As we can see, the pattern found an opening quote `match:"`, then the text is consumed lazily till the other quote `match:'`, that closes the match.
2727

28-
To make sure that the pattern looks for the closing quote exactly the same as the opening one, we can make a groups of it and use the backreference.
28+
To make sure that the pattern looks for the closing quote exactly the same as the opening one, we can wrap it into a capturing group and use the backreference.
2929

3030
Here's the correct code:
3131

0 commit comments

Comments
 (0)
Please sign in to comment.