Skip to content

Commit f496f9c

Browse files
authored
Merge pull request #202 from javascript-tutorial/sync-039716de
Sync with upstream @ 039716d
2 parents 9760284 + 7912e05 commit f496f9c

File tree

11 files changed

+79
-138
lines changed

11 files changed

+79
-138
lines changed

1-js/01-getting-started/1-intro/article.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,8 +109,9 @@ Exemplos de tais linguagens:
109109
- [CoffeeScript](http://coffeescript.org/) é um "açúcar sintático" para JavaScript. Ele introduz uma sintaxe mais curta, permitindo-nos escrever um código mais claro e preciso. Normalmente, Ruby devs gostam dele.
110110
- [TypeScript](http://www.typescriptlang.org/) está concentrado em adicionar "estritos tipos de dados" para simplificar o desenvolvimento e suporte de sistemas complexos. É desenvolvido pela Microsoft.
111111
- [Flow](http://flow.org/) também adiciona tipos de dados, mas de uma forma diferente. Desenvolvido pela Facebook.
112-
- [Dart](https://www.dartlang.org/) é uma linguagem autônoma que tem seu próprio interpretador que roda em ambientes fora do navegador (como aplicativos móveis), mas também pode ser transpilada para JavaScript. Desenvolvido pela Google.
112+
- [Dart](https://www.dartlang.org/) é uma linguagem autônoma que tem seu próprio interpretador que roda em ambientes fora do navegador (como aplicativos móveis), mas também pode ser transpilada para JavaScript. Desenvolvida pela Google.
113113
- [Brython](https://brython.info/) é um transpilador de Python para JavaScript que permite escrever aplicativos em puro Python, sem JavaScript.
114+
- [Kotlin](https://kotlinlang.org/docs/js-overview.html) é uma linguagem de programação moderna, concisa e segura, que pode ser usada no navegador ou no Node.
114115

115116
Há mais. Claro que, mesmo que usemos uma dessas linguagens transpiladas, também devemos saber JavaScript para entender o que estamos fazendo.
116117

1-js/04-object-basics/07-optional-chaining/article.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -219,4 +219,4 @@ As we can see, all of them are straightforward and simple to use. The `?.` check
219219
220220
A chain of `?.` allows to safely access nested properties.
221221
222-
Still, we should apply `?.` carefully, only where it's acceptable that the left part doesn't to exist. So that it won't hide programming errors from us, if they occur.
222+
Still, we should apply `?.` carefully, only where it's acceptable that the left part doesn't exist. So that it won't hide programming errors from us, if they occur.

1-js/05-data-types/02-number/article.md

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ Imagine we need to write 1 billion. The obvious way is:
1616
let billion = 1000000000;
1717
```
1818

19-
But in real life we usually avoid writing a long string of zeroes as it's easy to mistype. Also, we are lazy. We will usually write something like `"1bn"` for a billion or `"7.3bn"` for 7 billion 300 million. The same is true for most large numbers.
19+
But in real life, we usually avoid writing a long string of zeroes as it's easy to mistype. Also, we are lazy. We will usually write something like `"1bn"` for a billion or `"7.3bn"` for 7 billion 300 million. The same is true for most large numbers.
2020

2121
In JavaScript, we shorten a number by appending the letter `"e"` to the number and specifying the zeroes count:
2222

@@ -180,7 +180,7 @@ There are two ways to do so:
180180

181181
## Imprecise calculations
182182

183-
Internally, a number is represented in 64-bit format [IEEE-754](http://en.wikipedia.org/wiki/IEEE_754-1985), so there are exactly 64 bits to store a number: 52 of them are used to store the digits, 11 of them store the position of the decimal point (they are zero for integer numbers), and 1 bit is for the sign.
183+
Internally, a number is represented in 64-bit format [IEEE-754](https://en.wikipedia.org/wiki/IEEE_754-2008_revision), so there are exactly 64 bits to store a number: 52 of them are used to store the digits, 11 of them store the position of the decimal point (they are zero for integer numbers), and 1 bit is for the sign.
184184

185185
If a number is too big, it would overflow the 64-bit storage, potentially giving an infinity:
186186

@@ -208,15 +208,15 @@ Ouch! There are more consequences than an incorrect comparison here. Imagine you
208208

209209
But why does this happen?
210210

211-
A number is stored in memory in its binary form, a sequence of ones and zeroes. But fractions like `0.1`, `0.2` that look simple in the decimal numeric system are actually unending fractions in their binary form.
211+
A number is stored in memory in its binary form, a sequence of bits - ones and zeroes. But fractions like `0.1`, `0.2` that look simple in the decimal numeric system are actually unending fractions in their binary form.
212212

213213
In other words, what is `0.1`? It is one divided by ten `1/10`, one-tenth. In decimal numeral system such numbers are easily representable. Compare it to one-third: `1/3`. It becomes an endless fraction `0.33333(3)`.
214214

215215
So, division by powers `10` is guaranteed to work well in the decimal system, but division by `3` is not. For the same reason, in the binary numeral system, the division by powers of `2` is guaranteed to work, but `1/10` becomes an endless binary fraction.
216216

217217
There's just no way to store *exactly 0.1* or *exactly 0.2* using the binary system, just like there is no way to store one-third as a decimal fraction.
218218

219-
The numeric format IEEE-754 solves this by rounding to the nearest possible number. These rounding rules normally don't allow us to see that "tiny precision loss", so the number shows up as `0.3`. But beware, the loss still exists.
219+
The numeric format IEEE-754 solves this by rounding to the nearest possible number. These rounding rules normally don't allow us to see that "tiny precision loss", but it exists.
220220

221221
We can see this in action:
222222
```js run
@@ -327,7 +327,7 @@ Please note that an empty or a space-only string is treated as `0` in all numeri
327327
There is a special built-in method [`Object.is`](mdn:js/Object/is) that compares values like `===`, but is more reliable for two edge cases:
328328

329329
1. It works with `NaN`: `Object.is(NaN, NaN) === true`, that's a good thing.
330-
2. Values `0` and `-0` are different: `Object.is(0, -0) === false`, it rarely matters, but these values technically are different.
330+
2. Values `0` and `-0` are different: `Object.is(0, -0) === false`, technically that's true, because internally the number has a sign bit that may be different even if all other bits are zeroes.
331331

332332
In all other cases, `Object.is(a, b)` is the same as `a === b`.
333333

@@ -383,7 +383,7 @@ JavaScript has a built-in [Math](https://developer.mozilla.org/en/docs/Web/JavaS
383383
A few examples:
384384

385385
`Math.random()`
386-
: Returns a random number from 0 to 1 (not including 1)
386+
: Returns a random number from 0 to 1 (not including 1).
387387

388388
```js run
389389
alert( Math.random() ); // 0.1234567894322
@@ -400,7 +400,7 @@ A few examples:
400400
```
401401

402402
`Math.pow(n, power)`
403-
: Returns `n` raised the given power
403+
: Returns `n` raised to the given power.
404404

405405
```js run
406406
alert( Math.pow(2, 10) ); // 2 in power 10 = 1024

1-js/05-data-types/06-iterable/article.md

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

44
*Iterable* objects are a generalization of arrays. That's a concept that allows us to make any object useable in a `for..of` loop.
55

6-
Of course, Arrays are iterable. But there are many other built-in objects, that are iterable as well. For instance, Strings are iterable also. As we'll see, many built-in operators and methods rely on them.
6+
Of course, Arrays are iterable. But there are many other built-in objects, that are iterable as well. For instance, strings are also iterable.
77

8-
If an object represents a collection (list, set) of something, then `for..of` is a great syntax to loop over it, so let's see how to make it work.
8+
If an object isn't technically an array, but represents a collection (list, set) of something, then `for..of` is a great syntax to loop over it, so let's see how to make it work.
99

1010

1111
## Symbol.iterator
@@ -31,9 +31,9 @@ To make the `range` object iterable (and thus let `for..of` work) we need to add
3131
1. When `for..of` starts, it calls that method once (or errors if not found). The method must return an *iterator* -- an object with the method `next`.
3232
2. Onward, `for..of` works *only with that returned object*.
3333
3. When `for..of` wants the next value, it calls `next()` on that object.
34-
4. The result of `next()` must have the form `{done: Boolean, value: any}`, where `done=true` means that the iteration is finished, otherwise `value` must be the new value.
34+
4. The result of `next()` must have the form `{done: Boolean, value: any}`, where `done=true` means that the iteration is finished, otherwise `value` is the next value.
3535

36-
Here's the full implementation for `range`:
36+
Here's the full implementation for `range` with remarks:
3737

3838
```js run
3939
let range = {
@@ -68,10 +68,10 @@ for (let num of range) {
6868
}
6969
```
7070

71-
Please note the core feature of iterables: an important separation of concerns:
71+
Please note the core feature of iterables: separation of concerns.
7272

7373
- The `range` itself does not have the `next()` method.
74-
- Instead, another object, a so-called "iterator" is created by the call to `range[Symbol.iterator]()`, and it handles the whole iteration.
74+
- Instead, another object, a so-called "iterator" is created by the call to `range[Symbol.iterator]()`, and its `next()` generates values for the iteration.
7575

7676
So, the iterator object is separate from the object it iterates over.
7777

@@ -105,7 +105,7 @@ for (let num of range) {
105105

106106
Now `range[Symbol.iterator]()` returns the `range` object itself: it has the necessary `next()` method and remembers the current iteration progress in `this.current`. Shorter? Yes. And sometimes that's fine too.
107107

108-
The downside is that now it's impossible to have two `for..of` loops running over the object simultaneously: they'll share the iteration state, because there's only one iterator -- the object itself. But two parallel for-ofs is a rare thing, doable with some async scenarios.
108+
The downside is that now it's impossible to have two `for..of` loops running over the object simultaneously: they'll share the iteration state, because there's only one iterator -- the object itself. But two parallel for-ofs is a rare thing, even in async scenarios.
109109

110110
```smart header="Infinite iterators"
111111
Infinite iterators are also possible. For instance, the `range` becomes infinite for `range.to = Infinity`. Or we can make an iterable object that generates an infinite sequence of pseudorandom numbers. Also can be useful.
@@ -174,7 +174,7 @@ When we use JavaScript for practical tasks in a browser or any other environment
174174

175175
For instance, strings are both iterable (`for..of` works on them) and array-like (they have numeric indexes and `length`).
176176

177-
But an iterable may not be array-like. And vice versa an array-like may not be iterable.
177+
But an iterable may be not array-like. And vice versa an array-like may be not iterable.
178178

179179
For example, the `range` in the example above is iterable, but not array-like, because it does not have indexed properties and `length`.
180180

@@ -193,11 +193,11 @@ for (let item of arrayLike) {}
193193
*/!*
194194
```
195195

196-
What do they have in common? Both iterables and array-likes are usually *not arrays*, they don't have `push`, `pop` etc. That's rather inconvenient if we have such an object and want to work with it as with an array.
196+
Both iterables and array-likes are usually *not arrays*, they don't have `push`, `pop` etc. That's rather inconvenient if we have such an object and want to work with it as with an array. E.g. we would like to work with `range` using array methods. How to achieve that?
197197

198198
## Array.from
199199

200-
There's a universal method [Array.from](mdn:js/Array/from) that brings them together. It takes an iterable or array-like value and makes a "real" `Array` from it. Then we can call array methods on it.
200+
There's a universal method [Array.from](mdn:js/Array/from) that takes an iterable or array-like value and makes a "real" `Array` from it. Then we can call array methods on it.
201201

202202
For instance:
203203

@@ -283,7 +283,7 @@ let str = '𝒳😂𩷶';
283283

284284
alert( slice(str, 1, 3) ); // 😂𩷶
285285

286-
// native method does not support surrogate pairs
286+
// the native method does not support surrogate pairs
287287
alert( str.slice(1, 3) ); // garbage (two pieces from different surrogate pairs)
288288
```
289289

1-js/06-advanced-functions/04-var/article.md

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,19 @@ alert(test); // true, the variable lives after if
4242
*/!*
4343
```
4444

45-
If we used `let test` on the 2nd line, then it wouldn't be visible to `alert`. But `var` ignores code blocks, so we've got a global `test`.
45+
As `var` ignores code blocks, we've got a global variable `test`.
46+
47+
If we used `let test` instead of `var test`, then the variable would only be visible inside `if`:
48+
49+
```js run
50+
if (true) {
51+
let test = true; // use "let"
52+
}
53+
54+
*!*
55+
alert(test); // ReferenceError: test is not defined
56+
*/!*
57+
```
4658

4759
The same thing for loops: `var` cannot be block- or loop-local:
4860

@@ -70,7 +82,7 @@ function sayHi() {
7082
}
7183

7284
sayHi();
73-
alert(phrase); // Error: phrase is not defined
85+
alert(phrase); // ReferenceError: phrase is not defined
7486
```
7587

7688
As we can see, `var` pierces through `if`, `for` or other code blocks. That's because a long time ago in JavaScript, blocks had no Lexical Environments, and `var` is a remnant of that.
@@ -216,7 +228,7 @@ The Function Expression is wrapped with parenthesis `(function {...})`, because
216228

217229
```js run
218230
// Tries to declare and immediately call a function
219-
function() { // <-- Error: Function statements require a function name
231+
function() { // <-- SyntaxError: Function statements require a function name
220232

221233
var message = "Hello";
222234

Lines changed: 17 additions & 93 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11

2-
# Microtasks and event loop
2+
# Microtasks
33

44
Promise handlers `.then`/`.catch`/`.finally` are always asynchronous.
55

@@ -10,20 +10,20 @@ Here's a demo:
1010
```js run
1111
let promise = Promise.resolve();
1212

13-
promise.then(() => alert("promise done"));
13+
promise.then(() => alert("promise done!"));
1414

1515
alert("code finished"); // this alert shows first
1616
```
1717

18-
If you run it, you see `code finished` first, and then `promise done`.
18+
If you run it, you see `code finished` first, and then `promise done!`.
1919

2020
That's strange, because the promise is definitely done from the beginning.
2121

2222
Why did the `.then` trigger afterwards? What's going on?
2323

24-
# Microtasks
24+
## Microtasks queue
2525

26-
Asynchronous tasks need proper management. For that, the ECMA standard specifies an internal queue `PromiseJobs`, more often referred to as the "microtask queue" (ES8 term).
26+
Asynchronous tasks need proper management. For that, the ECMA standard specifies an internal queue `PromiseJobs`, more often referred to as the "microtask queue" (V8 term).
2727

2828
As stated in the [specification](https://tc39.github.io/ecma262/#sec-jobs-and-job-queues):
2929

@@ -52,77 +52,6 @@ Promise.resolve()
5252

5353
Now the order is as intended.
5454

55-
## Event loop
56-
57-
In-browser JavaScript, as well as Node.js, is based on an *event loop*.
58-
59-
"Event loop" is a process when the engine sleeps and waits for events, then reacts on those and sleeps again.
60-
61-
Examples of events:
62-
- `mousemove`, a user moved their mouse.
63-
- `setTimeout` handler is to be called.
64-
- an external `<script src="...">` is loaded, ready to be executed.
65-
- a network operation, e.g. `fetch` is complete.
66-
- ...etc.
67-
68-
Things happen -- the engine handles them -- and waits for more to happen (while sleeping and consuming close to zero CPU).
69-
70-
![](eventLoop.svg)
71-
72-
As you can see, there's also a queue here. A so-called "macrotask queue" (v8 term).
73-
74-
When an event happens, while the engine is busy, its handling is enqueued.
75-
76-
For instance, while the engine is busy processing a network `fetch`, a user may move their mouse causing `mousemove`, and `setTimeout` may be due and so on, just as painted on the picture above.
77-
78-
Events from the macrotask queue are processed on "first come – first served" basis. When the engine browser finishes with `fetch`, it handles `mousemove` event, then `setTimeout` handler, and so on.
79-
80-
So far, quite simple, right? The engine is busy, so other tasks queue up.
81-
82-
Now the important stuff.
83-
84-
**Microtask queue has a higher priority than the macrotask queue.**
85-
86-
In other words, the engine first executes all microtasks, and then takes a macrotask. Promise handling always has the priority.
87-
88-
For instance, take a look:
89-
90-
```js run
91-
setTimeout(() => alert("timeout"));
92-
93-
Promise.resolve()
94-
.then(() => alert("promise"));
95-
96-
alert("code");
97-
```
98-
99-
What's the order?
100-
101-
1. `code` shows first, because it's a regular synchronous call.
102-
2. `promise` shows second, because `.then` passes through the microtask queue, and runs after the current code.
103-
3. `timeout` shows last, because it's a macrotask.
104-
105-
It may happen that while handling a macrotask, new promises are created.
106-
107-
Or, vice-versa, a microtask schedules a macrotask (e.g. `setTimeout`).
108-
109-
For instance, here `.then` schedules a `setTimeout`:
110-
111-
```js run
112-
Promise.resolve()
113-
.then(() => {
114-
setTimeout(() => alert("timeout"), 0);
115-
})
116-
.then(() => {
117-
alert("promise");
118-
});
119-
```
120-
121-
Naturally, `promise` shows up first, because `setTimeout` macrotask awaits in the less-priority macrotask queue.
122-
123-
As a logical consequence, macrotasks are handled only when promises give the engine a "free time". So if we have a promise chain that doesn't wait for anything, then things like `setTimeout` or event handlers can never get in the middle.
124-
125-
12655
## Unhandled rejection
12756

12857
Remember the `unhandledrejection` event from the article <info:promise-error-handling>?
@@ -131,34 +60,33 @@ Now we can see exactly how JavaScript finds out that there was an unhandled reje
13160

13261
**An "unhandled rejection" occurs when a promise error is not handled at the end of the microtask queue.**
13362

134-
For instance, consider this code:
63+
Normally, if we expect an error, we add `.catch` to the promise chain to handle it:
13564

13665
```js run
13766
let promise = Promise.reject(new Error("Promise Failed!"));
67+
*!*
68+
promise.catch(err => alert('caught'));
69+
*/!*
13870

139-
window.addEventListener('unhandledrejection', event => {
140-
alert(event.reason); // Promise Failed!
141-
});
71+
// doesn't run: error handled
72+
window.addEventListener('unhandledrejection', event => alert(event.reason));
14273
```
14374

14475
But if we forget to add `.catch`, then, after the microtask queue is empty, the engine triggers the event:
14576

14677
```js run
14778
let promise = Promise.reject(new Error("Promise Failed!"));
148-
*!*
149-
promise.catch(err => alert('caught'));
150-
*/!*
15179

152-
// no error, all quiet
80+
// Promise Failed!
15381
window.addEventListener('unhandledrejection', event => alert(event.reason));
15482
```
15583

156-
Now let's say, we'll be catching the error, but after `setTimeout`:
84+
What if we handle the error later? Like this:
15785

15886
```js run
15987
let promise = Promise.reject(new Error("Promise Failed!"));
16088
*!*
161-
setTimeout(() => promise.catch(err => alert('caught')));
89+
setTimeout(() => promise.catch(err => alert('caught')), 1000);
16290
*/!*
16391

16492
// Error: Promise Failed!
@@ -173,16 +101,12 @@ But now we understand that `unhandledrejection` is generated when the microtask
173101

174102
In the example above, `.catch` added by `setTimeout` also triggers. But it does so later, after `unhandledrejection` has already occurred, so it doesn't change anything.
175103

176-
**So, `.then/catch/finally` are called after the current code is finished.**
177-
178-
If we need to guarantee that a piece of code is executed after `.then/catch/finally`, it's best to add it into a chained `.then` call.
179-
180-
- There's also a "macrotask queue" that keeps various events, network operation results, `setTimeout`-scheduled calls, and so on. These are also called "macrotasks" (v8 term).
104+
## Summary
181105

182-
Promise handling is always asynchronous, as all promise actions pass through the internal "promise jobs" queue, also called "microtask queue" (ES8 term).
106+
Promise handling is always asynchronous, as all promise actions pass through the internal "promise jobs" queue, also called "microtask queue" (V8 term).
183107

184108
So `.then/catch/finally` handlers are always called after the current code is finished.
185109

186-
In other words, they have lower priority.
110+
If we need to guarantee that a piece of code is executed after `.then/catch/finally`, we can add it into a chained `.then` call.
187111

188112
In most Javascript engines, including browsers and Node.js, the concept of microtasks is closely tied with the "event loop" and "macrotasks". As these have no direct relation to promises, they are covered in another part of the tutorial, in the article <info:event-loop>.

0 commit comments

Comments
 (0)