You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: 1-js/02-first-steps/03-strict-mode/article.md
+10-2Lines changed: 10 additions & 2 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -53,9 +53,17 @@ For the future, when you use a browser console to test features, please note tha
53
53
54
54
Sometimes, when `use strict` makes a difference, you'll get incorrect results.
55
55
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:
57
57
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:
Copy file name to clipboardExpand all lines: 1-js/03-code-quality/04-ninja-code/article.md
+4-5Lines changed: 4 additions & 5 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -137,7 +137,7 @@ Instead, reuse existing names. Just write new values into them.
137
137
138
138
In a function try to use only variables passed as parameters.
139
139
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.
141
141
142
142
**An advanced variant of the approach is to covertly (!) replace the value with something alike in the middle of a loop or a function.**
143
143
@@ -155,7 +155,7 @@ function ninjaFunction(elem) {
155
155
156
156
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!
157
157
158
-
Seen in code regularly. Deadly effective even against an experienced ninja.
158
+
Seen in code regularly. Deadly effective even against an experienced ninja.
159
159
160
160
## Underscores for fun
161
161
@@ -169,8 +169,7 @@ A smart ninja puts underscores at one spot of code and evades them at other plac
169
169
170
170
Let everyone see how magnificent your entities are! Names like `superElement`, `megaFrame` and `niceItem` will definitely enlighten a reader.
171
171
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.
174
173
175
174
176
175
## Overlap outer variables
@@ -180,7 +179,7 @@ When in the light, can't see anything in the darkness.<br>
180
179
When in the darkness, can see everything in the light.
181
180
```
182
181
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.
Copy file name to clipboardExpand all lines: 1-js/06-advanced-functions/11-currying-partials/article.md
+10-15Lines changed: 10 additions & 15 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -170,7 +170,7 @@ To understand the benefits we definitely need a worthy real-life example.
170
170
171
171
Advanced currying allows the function to be both callable normally and partially.
172
172
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`:
174
174
175
175
```js
176
176
functionlog(date, importance, message) {
@@ -184,34 +184,29 @@ Let's curry it!
184
184
log =_.curry(log);
185
185
```
186
186
187
-
After that `log` still works the normal way:
188
-
189
-
```js
190
-
log(newDate(), "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:
Copy file name to clipboardExpand all lines: 1-js/11-async/03-promise-chaining/article.md
+2-2Lines changed: 2 additions & 2 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -146,7 +146,7 @@ Returning promises allows us to build chains of asynchronous actions.
146
146
147
147
## Example: loadScript
148
148
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:
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.
309
309
310
310
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.
Copy file name to clipboardExpand all lines: 1-js/11-async/07-microtask-queue/article.md
-1Lines changed: 0 additions & 1 deletion
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -124,7 +124,6 @@ Naturally, `promise` shows up first, because `setTimeout` macrotask awaits in th
124
124
125
125
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.
126
126
127
-
128
127
## Unhandled rejection
129
128
130
129
Remember "unhandled rejection" event from the chapter <info:promise-error-handling>?
Copy file name to clipboardExpand all lines: 1-js/12-generators-iterators/2-async-iterators-generators/article.md
+14-14Lines changed: 14 additions & 14 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -3,13 +3,13 @@
3
3
4
4
Asynchronous iterators allow to iterate over data that comes asynchronously, on-demand.
5
5
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.
7
7
8
8
## Async iterators
9
9
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.
11
11
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:
13
13
14
14
```js run
15
15
let range = {
@@ -101,7 +101,7 @@ let range = {
101
101
})()
102
102
```
103
103
104
-
As we can see, the components are similar to regular iterators:
104
+
As we can see, the structure is similar to regular iterators:
105
105
106
106
1. To make an object asynchronously iterable, it must have a method `Symbol.asyncIterator``(1)`.
107
107
2. It must return the object with `next()` method returning a promise `(2)`.
@@ -117,22 +117,22 @@ Here's a small cheatsheet:
117
117
| to loop, use |`for..of`|`for await..of`|
118
118
119
119
120
-
````warn header="The spread operator doesn't work asynchronously"
120
+
````warn header="The spread operator ... doesn't work asynchronously"
121
121
Features that require regular, synchronous iterators, don't work with asynchronous ones.
122
122
123
123
For instance, a spread operator won't work:
124
124
```js
125
125
alert( [...range] ); // Error, no Symbol.iterator
126
126
```
127
127
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`.
129
129
````
130
130
131
131
## Async generators
132
132
133
-
JavaScript also provides generators, that are also iterable.
133
+
As we already know, JavaScript also supprots generators, and they are iterable.
134
134
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`:
136
136
137
137
```js run
138
138
function*generateSequence(start, end) {
@@ -147,7 +147,7 @@ for(let value of generateSequence(1, 5)) {
147
147
```
148
148
149
149
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.
151
151
152
152
But what if we need to use `await` in the generator body? To perform network requests, for instance.
153
153
@@ -184,15 +184,15 @@ It's indeed very simple. We add the `async` keyword, and the generator now can u
184
184
185
185
Technically, another the difference of an async generator is that its `generator.next()` method is now asynchronous also, it returns promises.
186
186
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:
188
188
189
189
```js
190
190
result =awaitgenerator.next(); // result = {value: ..., done: true/false}
191
191
```
192
192
193
193
## Iterables via async generators
194
194
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.
196
196
197
197
```js
198
198
let range = {
@@ -270,7 +270,7 @@ The pattern is very common, it's not about users, but just about anything. For i
270
270
- It responds with a JSON of 30 commits, and also provides a link to the next page in the `Link` header.
271
271
- Then we can use that link for the next request, to get more commits, and so on.
272
272
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:
274
274
275
275
```js
276
276
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):
332
332
})();
333
333
```
334
334
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.
336
336
337
337
## Summary
338
338
@@ -356,6 +356,6 @@ Syntax differences between async and regular generators:
356
356
357
357
In web-development we often meet streams of data, when it flows chunk-by-chunk. For instance, downloading or uploading a big file.
358
358
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).
360
360
361
361
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.
Copy file name to clipboardExpand all lines: 2-ui/1-document/08-styles-and-classes/article.md
+3-3Lines changed: 3 additions & 3 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -66,10 +66,10 @@ So we can operate both on the full class string using `className` or on individu
66
66
Methods of `classList`:
67
67
68
68
-`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.
70
70
-`elem.classList.contains("class")` -- returns `true/false`, checks for the given class.
71
71
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:
73
73
74
74
```html run
75
75
<bodyclass="main page">
@@ -147,7 +147,7 @@ To set the full style as a string, there's a special property `style.cssText`:
147
147
</script>
148
148
```
149
149
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.
151
151
152
152
The same can be accomplished by setting an attribute: `div.setAttribute('style', 'color: red...')`.
Copy file name to clipboardExpand all lines: 2-ui/5-loading/02-script-async-defer/article.md
+5-2Lines changed: 5 additions & 2 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -188,8 +188,11 @@ But there are also essential differences between them:
188
188
|`defer`|*Document order* (as they go in the document). | Execute after the document is loaded and parsed (they wait if needed), right before `DOMContentLoaded`. |
189
189
190
190
```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.
192
192
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
+
```
194
197
195
198
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.
Copy file name to clipboardExpand all lines: 6-data-storage/03-indexeddb/article.md
+39-42Lines changed: 39 additions & 42 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -16,11 +16,11 @@ That power is usually excessive for traditional client-server apps. IndexedDB is
16
16
17
17
The native interface to IndexedDB, described in the specification <https://www.w3.org/TR/IndexedDB>, is event-based.
18
18
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.
20
20
21
21
## Open database
22
22
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.
24
24
25
25
The syntax:
26
26
@@ -31,16 +31,16 @@ let openRequest = indexedDB.open(name, version);
31
31
-`name` -- a string, the database name.
32
32
-`version` -- a positive integer version, by default `1` (explained below).
33
33
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.
35
35
36
36
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.
38
38
-`error`: open failed.
39
39
-`upgradeneeded`: database version is outdated (see below).
40
40
41
41
**IndexedDB has a built-in mechanism of "schema versioning", absent in server-side databases.**
42
42
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.
44
44
45
45
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.
46
46
@@ -109,11 +109,13 @@ An example of object that can't be stored: an object with circular references. S
109
109
110
110
**There must be an unique `key` for every value in the store.**
111
111
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.
113
113
114
114

115
115
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.
117
119
118
120
The syntax to create an object store:
119
121
```js
@@ -127,7 +129,7 @@ Please note, the operation is synchronous, no `await` needed.
127
129
-`keyPath` -- a path to an object property that IndexedDB will use as the key, e.g. `id`.
128
130
-`autoIncrement` -- if `true`, then the key for a newly stored object is generated automatically, as an ever-incrementing number.
129
131
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.
131
133
132
134
For instance, this object store uses `id` property as the key:
**An object store can only be created/modified while updating the DB version, in `upgradeneeded` handler.**
138
140
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.
140
146
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.
144
148
145
149
Here's the demo of the second approach:
146
150
147
151
```js
148
152
let openRequest =indexedDB.open("db", 1);
149
153
150
-
// create an object store for books if not exists
154
+
// create/upgrade the database without version checks
151
155
openRequest.onupgradeneeded=function() {
152
156
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
-`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.
189
193
-`type` – a transaction type, one of:
190
194
-`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.
192
196
193
197
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.
194
198
195
-
```smart header="What are transaction types for?"
199
+
```smart header="Why there exist different types of transactions?"
196
200
Performance is the reason why transactions need to be labeled either `readonly` and `readwrite`.
197
201
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.
199
203
```
200
204
201
205
After the transaction is created, we can add an item to the store, like this:
1. Create a transaction, mention all stores it's going to access, at `(1)`.
233
237
2. Get the store object using `transaction.objectStore(name)`, at `(2)`.
234
238
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.
236
240
237
241
Object stores support two methods to store a value:
238
242
@@ -242,30 +246,24 @@ Object stores support two methods to store a value:
242
246
-**add(value, [key])**
243
247
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.
244
248
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.
246
250
247
251
- The `request.result` for `add` is the key of the new object.
248
252
- The error is in `request.error` (if any).
249
253
250
-
## Transactions autocommit
254
+
## Transactions' autocommit
251
255
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?
253
257
254
258
The short answer is: we don't.
255
259
256
260
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.
257
261
258
262
**When all transaction requests are finished, and the [microtasks queue](info:microtask-queue) is empty, it is committed automatically.**
259
263
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.
262
265
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.
269
267
270
268
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.
271
269
@@ -331,9 +329,9 @@ That's to be expected, not only because of possible errors at our side, but also
331
329
332
330
**A failed request automatically aborts the transaction, canceling all its changes.**
333
331
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()`.
335
333
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:
337
335
338
336
```js
339
337
let transaction =db.transaction("books", "readwrite");
-`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).
612
609
613
610
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`.
614
611
@@ -672,7 +669,7 @@ So we have all the sweet "plain async code" and "try..catch" stuff.
672
669
673
670
### Error handling
674
671
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`.
676
673
677
674
An uncaught error becomes an "unhandled promise rejection" event on `window` object.
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.
692
689
693
690
For a promise wrapper and `async/await` the situation is the same.
Copy file name to clipboardExpand all lines: 9-regular-expressions/10-regexp-backreferences/article.md
+3-3Lines changed: 3 additions & 3 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -1,6 +1,6 @@
1
1
# Backreferences in pattern: \n and \k
2
2
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.
4
4
5
5
## Backreference by number: \n
6
6
@@ -12,7 +12,7 @@ We need to find a quoted string: either a single-quoted `subject:'...'` or a dou
12
12
13
13
How to look for them?
14
14
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!"`:
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.
27
27
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.
0 commit comments