From 023c0ecac65ee7a3c991f4ea186af863a096ee69 Mon Sep 17 00:00:00 2001 From: Rahul Rao <63695122+rahulrao0209@users.noreply.github.com> Date: Sun, 16 Jul 2023 17:49:30 +0530 Subject: [PATCH 01/33] Fixing a minor grammatical typo in the document. Fixing a minor grammatical typo in the selection and range markdown document. --- 2-ui/99-ui-misc/02-selection-range/article.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/2-ui/99-ui-misc/02-selection-range/article.md b/2-ui/99-ui-misc/02-selection-range/article.md index 819bcba29..09a20bc67 100644 --- a/2-ui/99-ui-misc/02-selection-range/article.md +++ b/2-ui/99-ui-misc/02-selection-range/article.md @@ -354,7 +354,7 @@ The main selection properties are: ```smart header="Selection end/start vs Range" -There's an important differences of a selection anchor/focus compared with a `Range` start/end. +There's an important difference between a selection anchor/focus compared with a `Range` start/end. As we know, `Range` objects always have their start before the end. From d51037aadbd8ef31cebf11549583a4b9a504f23d Mon Sep 17 00:00:00 2001 From: nakhodkin <14351638+nakhodkin@users.noreply.github.com> Date: Wed, 27 Dec 2023 20:03:05 +0200 Subject: [PATCH 02/33] Fix grammar and add an example --- 1-js/05-data-types/02-number/article.md | 34 +++++++++++++++---------- 1 file changed, 20 insertions(+), 14 deletions(-) diff --git a/1-js/05-data-types/02-number/article.md b/1-js/05-data-types/02-number/article.md index c704bd980..a96e4e688 100644 --- a/1-js/05-data-types/02-number/article.md +++ b/1-js/05-data-types/02-number/article.md @@ -4,7 +4,7 @@ In modern JavaScript, there are two types of numbers: 1. Regular numbers in JavaScript are stored in 64-bit format [IEEE-754](https://en.wikipedia.org/wiki/IEEE_754), also known as "double precision floating point numbers". These are numbers that we're using most of the time, and we'll talk about them in this chapter. -2. BigInt numbers represent integers of arbitrary length. They are sometimes needed because a regular integer number can't safely exceed (253-1) or be less than -(253-1), as we mentioned earlier in the chapter . As bigints are used in few special areas, we devote them a special chapter . +2. BigInt numbers represent integers of arbitrary length. They are sometimes needed because a regular integer number can't safely exceed (253-1) or be less than -(253-1), as we mentioned earlier in the chapter . As bigints are used in a few special areas, we devote them to a special chapter . So here we'll talk about regular numbers. Let's expand our knowledge of them. @@ -41,7 +41,7 @@ In other words, `e` multiplies the number by `1` with the given zeroes count. 1.23e6 === 1.23 * 1000000; // e6 means *1000000 ``` -Now let's write something very small. Say, 1 microsecond (one millionth of a second): +Now let's write something very small. Say, 1 microsecond (one-millionth of a second): ```js let mсs = 0.000001; @@ -103,13 +103,13 @@ alert( num.toString(16) ); // ff alert( num.toString(2) ); // 11111111 ``` -The `base` can vary from `2` to `36`. By default it's `10`. +The `base` can vary from `2` to `36`. By default, it's `10`. Common use cases for this are: - **base=16** is used for hex colors, character encodings etc, digits can be `0..9` or `A..F`. - **base=2** is mostly for debugging bitwise operations, digits can be `0` or `1`. -- **base=36** is the maximum, digits can be `0..9` or `A..Z`. The whole latin alphabet is used to represent a number. A funny, but useful case for `36` is when we need to turn a long numeric identifier into something shorter, for example to make a short url. Can simply represent it in the numeral system with base `36`: +- **base=36** is the maximum, digits can be `0..9` or `A..Z`. The whole Latin alphabet is used to represent a number. A funny, but useful case for `36` is when we need to turn a long numeric identifier into something shorter, for example, to make a short url. Can simply represent it in the numeral system with base `36`: ```js run alert( 123456..toString(36) ); // 2n9c @@ -188,7 +188,7 @@ There are two ways to do so: alert( num.toFixed(5) ); // "12.34000", added zeroes to make exactly 5 digits ``` - We can convert it to a number using the unary plus or a `Number()` call, e.g write `+num.toFixed(5)`. + We can convert it to a number using the unary plus or a `Number()` call, e.g. write `+num.toFixed(5)`. ## Imprecise calculations @@ -222,7 +222,13 @@ But why does this happen? 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. -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)`. +```js run +alert(0.1.toString(2)); // 0.0001100110011001100110011001100110011001100110011001101 +alert(0.2.toString(2)); // 0.001100110011001100110011001100110011001100110011001101 +alert((0.1 + 0.2).toString(2)); // 0.0100110011001100110011001100110011001100110011001101 +``` + +What is `0.1`? It is one divided by ten `1/10`, one-tenth. In the decimal numeral system, such numbers are easily representable. Compare it to one-third: `1/3`. It becomes an endless fraction `0.33333(3)`. 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. @@ -242,7 +248,7 @@ That's why `0.1 + 0.2` is not exactly `0.3`. ```smart header="Not only JavaScript" The same issue exists in many other programming languages. -PHP, Java, C, Perl, Ruby give exactly the same result, because they are based on the same numeric format. +PHP, Java, C, Perl, and Ruby give exactly the same result, because they are based on the same numeric format. ``` Can we work around the problem? Sure, the most reliable method is to round the result with the help of a method [toFixed(n)](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/toFixed): @@ -266,7 +272,7 @@ alert( (0.1 * 10 + 0.2 * 10) / 10 ); // 0.3 alert( (0.28 * 100 + 0.14 * 100) / 100); // 0.4200000000000001 ``` -So, multiply/divide approach reduces the error, but doesn't remove it totally. +So, the multiply/divide approach reduces the error, but doesn't remove it totally. Sometimes we could try to evade fractions at all. Like if we're dealing with a shop, then we can store prices in cents instead of dollars. But what if we apply a discount of 30%? In practice, totally evading fractions is rarely possible. Just round them to cut "tails" when needed. @@ -288,7 +294,7 @@ Another funny consequence of the internal representation of numbers is the exist That's because a sign is represented by a single bit, so it can be set or not set for any number including a zero. -In most cases the distinction is unnoticeable, because operators are suited to treat them as the same. +In most cases, the distinction is unnoticeable, because operators are suited to treat them as the same. ``` ## Tests: isFinite and isNaN @@ -337,7 +343,7 @@ Please note that an empty or a space-only string is treated as `0` in all numeri ````smart header="`Number.isNaN` and `Number.isFinite`" [Number.isNaN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/isNaN) and [Number.isFinite](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/isFinite) methods are the more "strict" versions of `isNaN` and `isFinite` functions. They do not autoconvert their argument into a number, but check if it belongs to the `number` type instead. -- `Number.isNaN(value)` returns `true` if the argument belongs to the `number` type and it is `NaN`. In any other case it returns `false`. +- `Number.isNaN(value)` returns `true` if the argument belongs to the `number` type and it is `NaN`. In any other case, it returns `false`. ```js run alert( Number.isNaN(NaN) ); // true @@ -348,7 +354,7 @@ Please note that an empty or a space-only string is treated as `0` in all numeri alert( isNaN("str") ); // true, because isNaN converts string "str" into a number and gets NaN as a result of this conversion ``` -- `Number.isFinite(value)` returns `true` if the argument belongs to the `number` type and it is not `NaN/Infinity/-Infinity`. In any other case it returns `false`. +- `Number.isFinite(value)` returns `true` if the argument belongs to the `number` type and it is not `NaN/Infinity/-Infinity`. In any other case, it returns `false`. ```js run alert( Number.isFinite(123) ); // true @@ -367,7 +373,7 @@ In a way, `Number.isNaN` and `Number.isFinite` are simpler and more straightforw There is a special built-in method `Object.is` that compares values like `===`, but is more reliable for two edge cases: 1. It works with `NaN`: `Object.is(NaN, NaN) === true`, that's a good thing. -2. Values `0` and `-0` are different: `Object.is(0, -0) === false`, technically that's correct, because internally the number has a sign bit that may be different even if all other bits are zeroes. +2. Values `0` and `-0` are different: `Object.is(0, -0) === false`, technically that's correct because internally the number has a sign bit that may be different even if all other bits are zeroes. In all other cases, `Object.is(a, b)` is the same as `a === b`. @@ -385,7 +391,7 @@ alert( +"100px" ); // NaN The sole exception is spaces at the beginning or at the end of the string, as they are ignored. -But in real life we often have values in units, like `"100px"` or `"12pt"` in CSS. Also in many countries the currency symbol goes after the amount, so we have `"19€"` and would like to extract a numeric value out of that. +But in real life, we often have values in units, like `"100px"` or `"12pt"` in CSS. Also in many countries, the currency symbol goes after the amount, so we have `"19€"` and would like to extract a numeric value out of that. That's what `parseInt` and `parseFloat` are for. @@ -479,4 +485,4 @@ For fractions: More mathematical functions: -- See the [Math](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Math) object when you need them. The library is very small, but can cover basic needs. +- See the [Math](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Math) object when you need them. The library is very small but can cover basic needs. From c66baceac6ff150d1d562f94ce82318ea731259d Mon Sep 17 00:00:00 2001 From: nakhodkin <14351638+nakhodkin@users.noreply.github.com> Date: Sun, 31 Dec 2023 20:49:11 +0200 Subject: [PATCH 03/33] Fix grammar and typos --- .../05-data-types/05-array-methods/article.md | 48 +++++++++---------- 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/1-js/05-data-types/05-array-methods/article.md b/1-js/05-data-types/05-array-methods/article.md index 4db1a16b6..853645958 100644 --- a/1-js/05-data-types/05-array-methods/article.md +++ b/1-js/05-data-types/05-array-methods/article.md @@ -1,6 +1,6 @@ # Array methods -Arrays provide a lot of methods. To make things easier, in this chapter they are split into groups. +Arrays provide a lot of methods. To make things easier, in this chapter, they are split into groups. ## Add/remove items @@ -32,11 +32,11 @@ alert( arr.length ); // 3 The element was removed, but the array still has 3 elements, we can see that `arr.length == 3`. -That's natural, because `delete obj.key` removes a value by the `key`. It's all it does. Fine for objects. But for arrays we usually want the rest of elements to shift and occupy the freed place. We expect to have a shorter array now. +That's natural, because `delete obj.key` removes a value by the `key`. It's all it does. Fine for objects. But for arrays we usually want the rest of the elements to shift and occupy the freed place. We expect to have a shorter array now. So, special methods should be used. -The [arr.splice](mdn:js/Array/splice) method is a swiss army knife for arrays. It can do everything: insert, remove and replace elements. +The [arr.splice](mdn:js/Array/splice) method is a Swiss army knife for arrays. It can do everything: insert, remove and replace elements. The syntax is: @@ -62,7 +62,7 @@ alert( arr ); // ["I", "JavaScript"] Easy, right? Starting from the index `1` it removed `1` element. -In the next example we remove 3 elements and replace them with the other two: +In the next example, we remove 3 elements and replace them with the other two: ```js run let arr = [*!*"I", "study", "JavaScript",*/!* "right", "now"]; @@ -84,7 +84,7 @@ let removed = arr.splice(0, 2); alert( removed ); // "I", "study" <-- array of removed elements ``` -The `splice` method is also able to insert the elements without any removals. For that we need to set `deleteCount` to `0`: +The `splice` method is also able to insert the elements without any removals. For that, we need to set `deleteCount` to `0`: ```js run let arr = ["I", "study", "JavaScript"]; @@ -114,7 +114,7 @@ alert( arr ); // 1,2,3,4,5 ### slice -The method [arr.slice](mdn:js/Array/slice) is much simpler than similar-looking `arr.splice`. +The method [arr.slice](mdn:js/Array/slice) is much simpler than the similar-looking `arr.splice`. The syntax is: @@ -124,7 +124,7 @@ arr.slice([start], [end]) It returns a new array copying to it all items from index `start` to `end` (not including `end`). Both `start` and `end` can be negative, in that case position from array end is assumed. -It's similar to a string method `str.slice`, but instead of substrings it makes subarrays. +It's similar to a string method `str.slice`, but instead of substrings, it makes subarrays. For instance: @@ -206,7 +206,7 @@ The [arr.forEach](mdn:js/Array/forEach) method allows to run a function for ever The syntax: ```js arr.forEach(function(item, index, array) { - // ... do something with item + // ... do something with an item }); ``` @@ -239,7 +239,7 @@ The methods [arr.indexOf](mdn:js/Array/indexOf) and [arr.includes](mdn:js/Array/ - `arr.indexOf(item, from)` -- looks for `item` starting from index `from`, and returns the index where it was found, otherwise `-1`. - `arr.includes(item, from)` -- looks for `item` starting from index `from`, returns `true` if found. -Usually these methods are used with only one argument: the `item` to search. By default, the search is from the beginning. +Usually, these methods are used with only one argument: the `item` to search. By default, the search is from the beginning. For instance: @@ -255,7 +255,7 @@ alert( arr.includes(1) ); // true Please note that `indexOf` uses the strict equality `===` for comparison. So, if we look for `false`, it finds exactly `false` and not the zero. -If we want to check if `item` exists in the array, and don't need the index, then `arr.includes` is preferred. +If we want to check if `item` exists in the array and don't need the index, then `arr.includes` is preferred. The method [arr.lastIndexOf](mdn:js/Array/lastIndexOf) is the same as `indexOf`, but looks for from right to left. @@ -274,12 +274,12 @@ const arr = [NaN]; alert( arr.indexOf(NaN) ); // -1 (wrong, should be 0) alert( arr.includes(NaN) );// true (correct) ``` -That's because `includes` was added to JavaScript much later and uses the more up to date comparison algorithm internally. +That's because `includes` was added to JavaScript much later and uses the more up-to-date comparison algorithm internally. ```` ### find and findIndex/findLastIndex -Imagine we have an array of objects. How do we find an object with the specific condition? +Imagine we have an array of objects. How do we find an object with a specific condition? Here the [arr.find(fn)](mdn:js/Array/find) method comes in handy. @@ -297,7 +297,7 @@ The function is called for elements of the array, one after another: - `index` is its index. - `array` is the array itself. -If it returns `true`, the search is stopped, the `item` is returned. If nothing found, `undefined` is returned. +If it returns `true`, the search is stopped, the `item` is returned. If nothing is found, `undefined` is returned. For example, we have an array of users, each with the fields `id` and `name`. Let's find the one with `id == 1`: @@ -313,11 +313,11 @@ let user = users.find(item => item.id == 1); alert(user.name); // John ``` -In real life arrays of objects is a common thing, so the `find` method is very useful. +In real life, arrays of objects are a common thing, so the `find` method is very useful. Note that in the example we provide to `find` the function `item => item.id == 1` with one argument. That's typical, other arguments of this function are rarely used. -The [arr.findIndex](mdn:js/Array/findIndex) method has the same syntax, but returns the index where the element was found instead of the element itself. The value of `-1` is returned if nothing is found. +The [arr.findIndex](mdn:js/Array/findIndex) method has the same syntax but returns the index where the element was found instead of the element itself. The value of `-1` is returned if nothing is found. The [arr.findLastIndex](mdn:js/Array/findLastIndex) method is like `findIndex`, but searches from right to left, similar to `lastIndexOf`. @@ -450,11 +450,11 @@ alert(arr); // *!*1, 2, 15*/!* Now it works as intended. -Let's step aside and think what's happening. The `arr` can be array of anything, right? It may contain numbers or strings or objects or whatever. We have a set of *some items*. To sort it, we need an *ordering function* that knows how to compare its elements. The default is a string order. +Let's step aside and think about what's happening. The `arr` can be an array of anything, right? It may contain numbers or strings or objects or whatever. We have a set of *some items*. To sort it, we need an *ordering function* that knows how to compare its elements. The default is a string order. The `arr.sort(fn)` method implements a generic sorting algorithm. We don't need to care how it internally works (an optimized [quicksort](https://en.wikipedia.org/wiki/Quicksort) or [Timsort](https://en.wikipedia.org/wiki/Timsort) most of the time). It will walk the array, compare its elements using the provided function and reorder them, all we need is to provide the `fn` which does the comparison. -By the way, if we ever want to know which elements are compared -- nothing prevents from alerting them: +By the way, if we ever want to know which elements are compared -- nothing prevents us from alerting them: ```js run [1, -2, 15, 2, 0, 8].sort(function(a, b) { @@ -526,7 +526,7 @@ Here's the situation from real life. We are writing a messaging app, and the per The [str.split(delim)](mdn:js/String/split) method does exactly that. It splits the string into an array by the given delimiter `delim`. -In the example below, we split by a comma followed by space: +In the example below, we split by a comma followed by a space: ```js run let names = 'Bilbo, Gandalf, Nazgul'; @@ -593,9 +593,9 @@ Arguments: - `index` -- is its position. - `array` -- is the array. -As function is applied, the result of the previous function call is passed to the next one as the first argument. +As the function is applied, the result of the previous function call is passed to the next one as the first argument. -So, the first argument is essentially the accumulator that stores the combined result of all previous executions. And at the end it becomes the result of `reduce`. +So, the first argument is essentially the accumulator that stores the combined result of all previous executions. And at the end, it becomes the result of `reduce`. Sounds complicated? @@ -664,7 +664,7 @@ arr.reduce((sum, current) => sum + current); So it's advised to always specify the initial value. -The method [arr.reduceRight](mdn:js/Array/reduceRight) does the same, but goes from right to left. +The method [arr.reduceRight](mdn:js/Array/reduceRight) does the same but goes from right to left. ## Array.isArray @@ -689,7 +689,7 @@ alert(Array.isArray([])); // true Almost all array methods that call functions -- like `find`, `filter`, `map`, with a notable exception of `sort`, accept an optional additional parameter `thisArg`. -That parameter is not explained in the sections above, because it's rarely used. But for completeness we have to cover it. +That parameter is not explained in the sections above, because it's rarely used. But for completeness, we have to cover it. Here's the full syntax of these methods: @@ -749,7 +749,7 @@ A cheat sheet of array methods: - `concat(...items)` -- returns a new array: copies all members of the current one and adds `items` to it. If any of `items` is an array, then its elements are taken. - To search among elements: - - `indexOf/lastIndexOf(item, pos)` -- look for `item` starting from position `pos`, return the index or `-1` if not found. + - `indexOf/lastIndexOf(item, pos)` -- look for `item` starting from position `pos`, and return the index or `-1` if not found. - `includes(value)` -- returns `true` if the array has `value`, otherwise `false`. - `find/filter(func)` -- filter elements through the function, return first/all values that make it return `true`. - `findIndex` is like `find`, but returns the index instead of a value. @@ -795,7 +795,7 @@ These methods are the most used ones, they cover 99% of use cases. But there are For the full list, see the [manual](mdn:js/Array). -From the first sight it may seem that there are so many methods, quite difficult to remember. But actually that's much easier. +At first sight, it may seem that there are so many methods, quite difficult to remember. But actually, that's much easier. Look through the cheat sheet just to be aware of them. Then solve the tasks of this chapter to practice, so that you have experience with array methods. From bbac8a5d1fd9a7a1dba353f2530e030eb0276133 Mon Sep 17 00:00:00 2001 From: nakhodkin <14351638+nakhodkin@users.noreply.github.com> Date: Wed, 3 Jan 2024 00:35:56 +0200 Subject: [PATCH 04/33] Fix grammar and JavaScript syntax --- .../10-destructuring-assignment/article.md | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/1-js/05-data-types/10-destructuring-assignment/article.md b/1-js/05-data-types/10-destructuring-assignment/article.md index 41e36db2c..0a37fd938 100644 --- a/1-js/05-data-types/10-destructuring-assignment/article.md +++ b/1-js/05-data-types/10-destructuring-assignment/article.md @@ -5,7 +5,7 @@ The two most used data structures in JavaScript are `Object` and `Array`. - Objects allow us to create a single entity that stores data items by key. - Arrays allow us to gather data items into an ordered list. -Although, when we pass those to a function, it may need not be an object/array as a whole. It may need individual pieces. +However, when we pass those to a function, it may need not to be an object/array as a whole. It may need individual pieces. *Destructuring assignment* is a special syntax that allows us to "unpack" arrays or objects into a bunch of variables, as sometimes that's more convenient. @@ -418,7 +418,7 @@ alert( title ); // Menu ## Nested destructuring -If an object or an array contain other nested objects and arrays, we can use more complex left-side patterns to extract deeper portions. +If an object or an array contains other nested objects and arrays, we can use more complex left-side patterns to extract deeper portions. In the code below `options` has another object in the property `size` and an array in the property `items`. The pattern on the left side of the assignment has the same structure to extract values from them: @@ -449,7 +449,7 @@ alert(item1); // Cake alert(item2); // Donut ``` -All properties of `options` object except `extra` that is absent in the left part, are assigned to corresponding variables: +All properties of `options` object except `extra` which is absent in the left part, are assigned to corresponding variables: ![](destructuring-complex.svg) @@ -459,9 +459,9 @@ Note that there are no variables for `size` and `items`, as we take their conten ## Smart function parameters -There are times when a function has many parameters, most of which are optional. That's especially true for user interfaces. Imagine a function that creates a menu. It may have a width, a height, a title, items list and so on. +There are times when a function has many parameters, most of which are optional. That's especially true for user interfaces. Imagine a function that creates a menu. It may have a width, a height, a title, an item list and so on. -Here's a bad way to write such function: +Here's a bad way to write such a function: ```js function showMenu(title = "Untitled", width = 200, height = 100, items = []) { @@ -469,7 +469,7 @@ function showMenu(title = "Untitled", width = 200, height = 100, items = []) { } ``` -In real-life, the problem is how to remember the order of arguments. Usually IDEs try to help us, especially if the code is well-documented, but still... Another problem is how to call a function when most parameters are ok by default. +In real-life, the problem is how to remember the order of arguments. Usually, IDEs try to help us, especially if the code is well-documented, but still... Another problem is how to call a function when most parameters are ok by default. Like this? @@ -534,7 +534,7 @@ function({ }) ``` -Then, for an object of parameters, there will be a variable `varName` for property `incomingProperty`, with `defaultValue` by default. +Then, for an object of parameters, there will be a variable `varName` for the property `incomingProperty`, with `defaultValue` by default. Please note that such destructuring assumes that `showMenu()` does have an argument. If we want all values by default, then we should specify an empty object: @@ -561,7 +561,7 @@ In the code above, the whole arguments object is `{}` by default, so there's alw - Destructuring assignment allows for instantly mapping an object or array onto many variables. - The full object syntax: ```js - let {prop : varName = default, ...rest} = object + let {prop : varName = defaultValue, ...rest} = object ``` This means that property `prop` should go into the variable `varName` and, if no such property exists, then the `default` value should be used. @@ -571,9 +571,9 @@ In the code above, the whole arguments object is `{}` by default, so there's alw - The full array syntax: ```js - let [item1 = default, item2, ...rest] = array + let [item1 = defaultValue, item2, ...rest] = array ``` - The first item goes to `item1`; the second goes into `item2`, all the rest makes the array `rest`. + The first item goes to `item1`; the second goes into `item2`, and all the rest makes the array `rest`. - It's possible to extract data from nested arrays/objects, for that the left side must have the same structure as the right one. From ea05aa90a39338bf7baeab96b6e078335f165617 Mon Sep 17 00:00:00 2001 From: Philip Gromov <43331414+Filin3@users.noreply.github.com> Date: Mon, 1 Apr 2024 00:00:57 +0500 Subject: [PATCH 05/33] Updated result visualization --- 1-js/04-object-basics/09-object-toprimitive/article.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/1-js/04-object-basics/09-object-toprimitive/article.md b/1-js/04-object-basics/09-object-toprimitive/article.md index 0a16b5399..fa68da583 100644 --- a/1-js/04-object-basics/09-object-toprimitive/article.md +++ b/1-js/04-object-basics/09-object-toprimitive/article.md @@ -253,7 +253,7 @@ let obj = { } }; -alert(obj + 2); // 22 ("2" + 2), conversion to primitive returned a string => concatenation +alert(obj + 2); // "22" ("2" + 2), conversion to primitive returned a string => concatenation ``` ## Summary From 85da6f1701cf870b689de1a1bd42fa0b4857c84f Mon Sep 17 00:00:00 2001 From: ellie-heidari <56841406+ellie-heidari@users.noreply.github.com> Date: Fri, 10 May 2024 12:01:51 +0200 Subject: [PATCH 06/33] Update article.md Small change in the sentence. --- 1-js/05-data-types/06-iterable/article.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/1-js/05-data-types/06-iterable/article.md b/1-js/05-data-types/06-iterable/article.md index 76f74036c..e2c0d4f97 100644 --- a/1-js/05-data-types/06-iterable/article.md +++ b/1-js/05-data-types/06-iterable/article.md @@ -174,7 +174,7 @@ When we use JavaScript for practical tasks in a browser or any other environment For instance, strings are both iterable (`for..of` works on them) and array-like (they have numeric indexes and `length`). -But an iterable may be not array-like. And vice versa an array-like may be not iterable. +But an iterable may not be array-like. And vice versa an array-like may not be iterable. For example, the `range` in the example above is iterable, but not array-like, because it does not have indexed properties and `length`. From 475899e76726b120a3371ed26a44984ab655a138 Mon Sep 17 00:00:00 2001 From: "Stanislav (Stanley) Modrak" <44023416+smith558@users.noreply.github.com> Date: Fri, 17 May 2024 14:56:19 +0100 Subject: [PATCH 07/33] Update article.md --- 2-ui/99-ui-misc/03-event-loop/article.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/2-ui/99-ui-misc/03-event-loop/article.md b/2-ui/99-ui-misc/03-event-loop/article.md index 3ea0c2c57..c0d39f958 100644 --- a/2-ui/99-ui-misc/03-event-loop/article.md +++ b/2-ui/99-ui-misc/03-event-loop/article.md @@ -17,7 +17,7 @@ The general algorithm of the engine: - execute them, starting with the oldest task. 2. Sleep until a task appears, then go to 1. -That's a formalization for what we see when browsing a page. The JavaScript engine does nothing most of the time, it only runs if a script/handler/event activates. +That's a formalization of what we see when browsing a page. The JavaScript engine does nothing most of the time, it only runs if a script/handler/event activates. Examples of tasks: @@ -30,19 +30,19 @@ Tasks are set -- the engine handles them -- then waits for more tasks (while sle It may happen that a task comes while the engine is busy, then it's enqueued. -The tasks form a queue, so-called "macrotask queue" (v8 term): +The tasks form a queue, the so-called "macrotask queue" (v8 term): ![](eventLoop.svg) -For instance, while the engine is busy executing a `script`, a user may move their mouse causing `mousemove`, and `setTimeout` may be due and so on, these tasks form a queue, as illustrated on the picture above. +For instance, while the engine is busy executing a `script`, a user may move their mouse causing `mousemove`, and `setTimeout` may be due and so on, these tasks form a queue, as illustrated in the picture above. -Tasks from the queue are processed on "first come – first served" basis. When the engine browser is done with the `script`, it handles `mousemove` event, then `setTimeout` handler, and so on. +Tasks from the queue are processed on a "first come – first served" basis. When the engine browser is done with the `script`, it handles `mousemove` event, then `setTimeout` handler, and so on. So far, quite simple, right? Two more details: 1. Rendering never happens while the engine executes a task. It doesn't matter if the task takes a long time. Changes to the DOM are painted only after the task is complete. -2. If a task takes too long, the browser can't do other tasks, such as processing user events. So after a time, it raises an alert like "Page Unresponsive", suggesting killing the task with the whole page. That happens when there are a lot of complex calculations or a programming error leading to an infinite loop. +2. If a task takes too long, the browser can't do other tasks, such as processing user events. So after some time, it raises an alert like "Page Unresponsive", suggesting killing the task with the whole page. That happens when there are a lot of complex calculations or a programming error leading to an infinite loop. That was the theory. Now let's see how we can apply that knowledge. @@ -54,7 +54,7 @@ For example, syntax-highlighting (used to colorize code examples on this page) i While the engine is busy with syntax highlighting, it can't do other DOM-related stuff, process user events, etc. It may even cause the browser to "hiccup" or even "hang" for a bit, which is unacceptable. -We can avoid problems by splitting the big task into pieces. Highlight first 100 lines, then schedule `setTimeout` (with zero-delay) for the next 100 lines, and so on. +We can avoid problems by splitting the big task into pieces. Highlight the first 100 lines, then schedule `setTimeout` (with zero-delay) for the next 100 lines, and so on. To demonstrate this approach, for the sake of simplicity, instead of text-highlighting, let's take a function that counts from `1` to `1000000000`. From 7e524bac0bedac9a08bbf2ef3d3efa0b701c14e6 Mon Sep 17 00:00:00 2001 From: "Stanislav (Stanley) Modrak" <44023416+smith558@users.noreply.github.com> Date: Fri, 17 May 2024 15:10:51 +0100 Subject: [PATCH 08/33] Add link --- 2-ui/99-ui-misc/03-event-loop/article.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/2-ui/99-ui-misc/03-event-loop/article.md b/2-ui/99-ui-misc/03-event-loop/article.md index c0d39f958..f33188491 100644 --- a/2-ui/99-ui-misc/03-event-loop/article.md +++ b/2-ui/99-ui-misc/03-event-loop/article.md @@ -30,7 +30,7 @@ Tasks are set -- the engine handles them -- then waits for more tasks (while sle It may happen that a task comes while the engine is busy, then it's enqueued. -The tasks form a queue, the so-called "macrotask queue" (v8 term): +The tasks form a queue, the so-called "macrotask queue" ([v8](https://v8.dev/) term): ![](eventLoop.svg) From 42851f41d31eff62adcb38da6fbcc44600871840 Mon Sep 17 00:00:00 2001 From: Prasenna Venkatesh Vepambedu Date: Sat, 18 May 2024 04:26:03 -0400 Subject: [PATCH 09/33] Update task.md --- .../04-object-methods/8-chain-calls/task.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/1-js/04-object-basics/04-object-methods/8-chain-calls/task.md b/1-js/04-object-basics/04-object-methods/8-chain-calls/task.md index a2a19c620..7d2ef8c15 100644 --- a/1-js/04-object-basics/04-object-methods/8-chain-calls/task.md +++ b/1-js/04-object-basics/04-object-methods/8-chain-calls/task.md @@ -4,7 +4,7 @@ importance: 2 # Chaining -There's a `ladder` object that allows to go up and down: +There's a `ladder` object that allows you to go up and down: ```js let ladder = { @@ -21,7 +21,7 @@ let ladder = { }; ``` -Now, if we need to make several calls in sequence, can do it like this: +Now, if we need to make several calls in sequence, we can do it like this: ```js ladder.up(); @@ -32,10 +32,10 @@ ladder.down(); ladder.showStep(); // 0 ``` -Modify the code of `up`, `down` and `showStep` to make the calls chainable, like this: +Modify the code of `up`, `down`, and `showStep` to make the calls chainable, like this: ```js ladder.up().up().down().showStep().down().showStep(); // shows 1 then 0 ``` -Such approach is widely used across JavaScript libraries. +Such an approach is widely used across JavaScript libraries. From f684d39c7339fdea52ac9159b8e0ec0e0f20c5b2 Mon Sep 17 00:00:00 2001 From: sneeed <55649412+sneeed@users.noreply.github.com> Date: Sat, 8 Jun 2024 14:52:05 +0200 Subject: [PATCH 10/33] change example element of multidimensional array When teaching this subject I found that an example where you can see for which array the first and the second value in the [] after matrix stand, helps understanding better. --- 1-js/05-data-types/04-array/article.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/1-js/05-data-types/04-array/article.md b/1-js/05-data-types/04-array/article.md index ee2e3d713..e71e86a5b 100644 --- a/1-js/05-data-types/04-array/article.md +++ b/1-js/05-data-types/04-array/article.md @@ -426,7 +426,7 @@ let matrix = [ [7, 8, 9] ]; -alert( matrix[1][1] ); // 5, the central element +alert( matrix[0][1] ); // 2, the second value of the first inner array ``` ## toString From c151e118ce74d921e1defd398018b4499e614fe8 Mon Sep 17 00:00:00 2001 From: Ilya Kantor Date: Thu, 13 Jun 2024 21:14:46 +0200 Subject: [PATCH 11/33] minor fixes --- .github/FUNDING.yml | 1 + BACKERS.md | 6 ++++++ 2 files changed, 7 insertions(+) create mode 100644 .github/FUNDING.yml create mode 100644 BACKERS.md diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 000000000..490051876 --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1 @@ +github: iliakan diff --git a/BACKERS.md b/BACKERS.md new file mode 100644 index 000000000..36b1532bc --- /dev/null +++ b/BACKERS.md @@ -0,0 +1,6 @@ + +# Sponsors and Supporters + +## Supporters + +- Ilya Zelenko From 3fd3f9871e0aacf6ecc76956cb729973c6dcf0e2 Mon Sep 17 00:00:00 2001 From: Aleksandras Date: Sun, 23 Jun 2024 21:40:20 +0300 Subject: [PATCH 12/33] - `run` --- 1-js/11-async/02-promise-basics/article.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/1-js/11-async/02-promise-basics/article.md b/1-js/11-async/02-promise-basics/article.md index 207fb2c8c..b15643f0a 100644 --- a/1-js/11-async/02-promise-basics/article.md +++ b/1-js/11-async/02-promise-basics/article.md @@ -46,7 +46,7 @@ Later we'll see how "fans" can subscribe to these changes. Here's an example of a promise constructor and a simple executor function with "producing code" that takes time (via `setTimeout`): -```js run +```js let promise = new Promise(function(resolve, reject) { // the function is executed automatically when the promise is constructed From d1ffe5d7a1fd342d0090aeeded85556cf0ada948 Mon Sep 17 00:00:00 2001 From: kricsleo <32707098+kricsleo@users.noreply.github.com> Date: Fri, 5 Jul 2024 10:27:16 +0800 Subject: [PATCH 13/33] docs: remove eval polyfill.io --- 1-js/03-code-quality/06-polyfills/article.md | 1 - 1 file changed, 1 deletion(-) diff --git a/1-js/03-code-quality/06-polyfills/article.md b/1-js/03-code-quality/06-polyfills/article.md index f7064d093..19404b7b5 100644 --- a/1-js/03-code-quality/06-polyfills/article.md +++ b/1-js/03-code-quality/06-polyfills/article.md @@ -73,7 +73,6 @@ JavaScript is a highly dynamic language. Scripts may add/modify any function, ev Two interesting polyfill libraries are: - [core js](https://github.com/zloirock/core-js) that supports a lot, allows to include only needed features. -- [polyfill.io](https://polyfill.io/) service that provides a script with polyfills, depending on the features and user's browser. ## Summary From d6e0376a8ce53bcc3b7b69d626a536280a799a6a Mon Sep 17 00:00:00 2001 From: FloffyGarlic <155742846+FloffyGarlic@users.noreply.github.com> Date: Tue, 9 Jul 2024 17:37:19 +0200 Subject: [PATCH 14/33] Remove BigInt IE incompatibility part (#3709) * Remove BigInt IE incompatibility part As Internet Explorer was retired years ago, this paragraph is not needed anymore, so I deleted that line. * Delete link to MDN BigInt compatibility table Unimportant link: almost total compatibility since the appearance of zhe `BigInt` type. --- 1-js/02-first-steps/05-types/article.md | 7 ------- 1 file changed, 7 deletions(-) diff --git a/1-js/02-first-steps/05-types/article.md b/1-js/02-first-steps/05-types/article.md index 26f3bcd53..04e8b2450 100644 --- a/1-js/02-first-steps/05-types/article.md +++ b/1-js/02-first-steps/05-types/article.md @@ -94,13 +94,6 @@ const bigInt = 1234567890123456789012345678901234567890n; As `BigInt` numbers are rarely needed, we don't cover them here, but devoted them a separate chapter . Read it when you need such big numbers. - -```smart header="Compatibility issues" -Right now, `BigInt` is supported in Firefox/Chrome/Edge/Safari, but not in IE. -``` - -You can check [*MDN* BigInt compatibility table](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt#Browser_compatibility) to know which versions of a browser are supported. - ## String A string in JavaScript must be surrounded by quotes. From 5a0df77a9a45b81b86324d3b5573a9b3c7eab5d9 Mon Sep 17 00:00:00 2001 From: shallow-beach <96891913+shallow-beach@users.noreply.github.com> Date: Wed, 10 Jul 2024 01:43:17 -0700 Subject: [PATCH 15/33] Update article.md detail `Math.round` behavior on negative numbers in middle case. --- 1-js/05-data-types/02-number/article.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/1-js/05-data-types/02-number/article.md b/1-js/05-data-types/02-number/article.md index a96e4e688..96a7b622a 100644 --- a/1-js/05-data-types/02-number/article.md +++ b/1-js/05-data-types/02-number/article.md @@ -137,7 +137,7 @@ There are several built-in functions for rounding: : Rounds up: `3.1` becomes `4`, and `-1.1` becomes `-1`. `Math.round` -: Rounds to the nearest integer: `3.1` becomes `3`, `3.6` becomes `4`, the middle case: `3.5` rounds up to `4` too. +: Rounds to the nearest integer: `3.1` becomes `3`, `3.6` becomes `4`. In the middle cases `3.5` rounds up to `4`, and `-3.5` rounds up to `-3`. `Math.trunc` (not supported by Internet Explorer) : Removes anything after the decimal point without rounding: `3.1` becomes `3`, `-1.1` becomes `-1`. @@ -147,8 +147,10 @@ Here's the table to summarize the differences between them: | | `Math.floor` | `Math.ceil` | `Math.round` | `Math.trunc` | |---|---------|--------|---------|---------| |`3.1`| `3` | `4` | `3` | `3` | +|`3.5`| `3` | `4` | `4` | `3` | |`3.6`| `3` | `4` | `4` | `3` | |`-1.1`| `-2` | `-1` | `-1` | `-1` | +|`-1.5`| `-2` | `-1` | `-1` | `-1` | |`-1.6`| `-2` | `-1` | `-2` | `-1` | From 6f08958b99c6e63417d5c480fc5f0384c502b79d Mon Sep 17 00:00:00 2001 From: tonybishnoi <53338071+tonybishnoi@users.noreply.github.com> Date: Thu, 10 Oct 2024 01:55:56 +0530 Subject: [PATCH 16/33] minor fix to function name written in explanation The code uses the function named 'askPassword' but the text above it explaining the error reason says 'ask' instead of 'askPassword' --- .../10-bind/5-question-use-bind/solution.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/1-js/06-advanced-functions/10-bind/5-question-use-bind/solution.md b/1-js/06-advanced-functions/10-bind/5-question-use-bind/solution.md index 403107ca6..4a381c0b4 100644 --- a/1-js/06-advanced-functions/10-bind/5-question-use-bind/solution.md +++ b/1-js/06-advanced-functions/10-bind/5-question-use-bind/solution.md @@ -1,5 +1,5 @@ -The error occurs because `ask` gets functions `loginOk/loginFail` without the object. +The error occurs because `askPassword` gets functions `loginOk/loginFail` without the object. When it calls them, they naturally assume `this=undefined`. From eedc262cea1ba991c067e4bc794592121b5aa9e2 Mon Sep 17 00:00:00 2001 From: Nick Roma <54879724+nikoandpiko@users.noreply.github.com> Date: Wed, 23 Oct 2024 00:45:18 +0900 Subject: [PATCH 17/33] Grammatical fix --- 1-js/03-code-quality/06-polyfills/article.md | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/1-js/03-code-quality/06-polyfills/article.md b/1-js/03-code-quality/06-polyfills/article.md index 19404b7b5..b6ea60820 100644 --- a/1-js/03-code-quality/06-polyfills/article.md +++ b/1-js/03-code-quality/06-polyfills/article.md @@ -71,9 +71,7 @@ if (!Math.trunc) { // if no such function JavaScript is a highly dynamic language. Scripts may add/modify any function, even built-in ones. -Two interesting polyfill libraries are: -- [core js](https://github.com/zloirock/core-js) that supports a lot, allows to include only needed features. - +One interesting polyfill library is [core-js](https://github.com/zloirock/core-js), which supports a wide range of features and allows you to include only the specific ones you need. ## Summary From 67833c9ccf241d5d7bbe4f678da1f6541cf163c1 Mon Sep 17 00:00:00 2001 From: zakingslayerv22 <11926638+zakingslayerv22@users.noreply.github.com> Date: Wed, 11 Dec 2024 02:05:05 -0800 Subject: [PATCH 18/33] Update article.md Suggested edit - to make the phrase clearer. --- 1-js/11-async/02-promise-basics/article.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/1-js/11-async/02-promise-basics/article.md b/1-js/11-async/02-promise-basics/article.md index b15643f0a..66d9538fc 100644 --- a/1-js/11-async/02-promise-basics/article.md +++ b/1-js/11-async/02-promise-basics/article.md @@ -222,7 +222,7 @@ The idea of `finally` is to set up a handler for performing cleanup/finalizing a E.g. stopping loading indicators, closing no longer needed connections, etc. -Think of it as a party finisher. No matter was a party good or bad, how many friends were in it, we still need (or at least should) do a cleanup after it. +Think of it as a party finisher. Irresepective of whether a party was good or bad, how many friends were in it, we still need (or at least should) do a cleanup after it. The code may look like this: From b36823a929a10675eebc2f62d2a680a1cbcc8d63 Mon Sep 17 00:00:00 2001 From: pj-szdm <50366990+pj-szdm@users.noreply.github.com> Date: Wed, 18 Dec 2024 10:34:02 +0100 Subject: [PATCH 19/33] better wording --- 1-js/05-data-types/02-number/article.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/1-js/05-data-types/02-number/article.md b/1-js/05-data-types/02-number/article.md index 96a7b622a..8e41f673d 100644 --- a/1-js/05-data-types/02-number/article.md +++ b/1-js/05-data-types/02-number/article.md @@ -118,7 +118,7 @@ Common use cases for this are: ```warn header="Two dots to call a method" Please note that two dots in `123456..toString(36)` is not a typo. If we want to call a method directly on a number, like `toString` in the example above, then we need to place two dots `..` after it. -If we placed a single dot: `123456.toString(36)`, then there would be an error, because JavaScript syntax implies the decimal part after the first dot. And if we place one more dot, then JavaScript knows that the decimal part is empty and now goes the method. +If we placed a single dot: `123456.toString(36)`, then there would be an error, because JavaScript syntax implies the decimal part after the first dot. And if we place one more dot, then JavaScript knows that the decimal part is empty and now uses the method. Also could write `(123456).toString(36)`. From dc1437872bc789e3ad3ba6a36bf3a845a0f43041 Mon Sep 17 00:00:00 2001 From: mhi1627 <168352351+mhi1627@users.noreply.github.com> Date: Wed, 22 Jan 2025 16:37:56 +0530 Subject: [PATCH 20/33] Update article.md the comment number is missing when the we mention line 2 --- 1-js/02-first-steps/16-function-expressions/article.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/1-js/02-first-steps/16-function-expressions/article.md b/1-js/02-first-steps/16-function-expressions/article.md index b952d5943..c6dd891bd 100644 --- a/1-js/02-first-steps/16-function-expressions/article.md +++ b/1-js/02-first-steps/16-function-expressions/article.md @@ -82,7 +82,7 @@ let sayHi = function() { // (1) create alert( "Hello" ); }; -let func = sayHi; +let func = sayHi; //(2) // ... ``` From 8b2a2f2c1c6d888f811cb0a2aa7b47eb2797b2d6 Mon Sep 17 00:00:00 2001 From: Aditya Girdhar <92713380+AdityaGirdhar@users.noreply.github.com> Date: Thu, 30 Jan 2025 20:39:38 +0530 Subject: [PATCH 21/33] Improve readability --- 1-js/03-code-quality/06-polyfills/article.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/1-js/03-code-quality/06-polyfills/article.md b/1-js/03-code-quality/06-polyfills/article.md index 19404b7b5..aa849d407 100644 --- a/1-js/03-code-quality/06-polyfills/article.md +++ b/1-js/03-code-quality/06-polyfills/article.md @@ -71,8 +71,7 @@ if (!Math.trunc) { // if no such function JavaScript is a highly dynamic language. Scripts may add/modify any function, even built-in ones. -Two interesting polyfill libraries are: -- [core js](https://github.com/zloirock/core-js) that supports a lot, allows to include only needed features. +An interesting polyfill library is [core js](https://github.com/zloirock/core-js) that supports a lot and allows to include only needed features. ## Summary From 011dd4f4b46eb1a1bb4475465a261595bfacf20a Mon Sep 17 00:00:00 2001 From: Gleb Date: Mon, 10 Feb 2025 20:59:08 +0200 Subject: [PATCH 22/33] Update article.md Correction of grammatical error --- 1-js/08-prototypes/04-prototype-methods/article.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/1-js/08-prototypes/04-prototype-methods/article.md b/1-js/08-prototypes/04-prototype-methods/article.md index 34b977e9f..9c5f1eb3d 100644 --- a/1-js/08-prototypes/04-prototype-methods/article.md +++ b/1-js/08-prototypes/04-prototype-methods/article.md @@ -116,7 +116,7 @@ alert(obj[key]); // [object Object], not "some value"! Here, if the user types in `__proto__`, the assignment in line 4 is ignored! -That could surely be surprising for a non-developer, but pretty understandable for us. The `__proto__` property is special: it must be either an object or `null`. A string can not become a prototype. That's why an assignment a string to `__proto__` is ignored. +That could surely be surprising for a non-developer, but pretty understandable for us. The `__proto__` property is special: it must be either an object or `null`. A string can not become a prototype. That's why assigning a string to `__proto__` is ignored. But we didn't *intend* to implement such behavior, right? We want to store key/value pairs, and the key named `"__proto__"` was not properly saved. So that's a bug! From 4b3474bc41202750875bab325371c6e4c01003c7 Mon Sep 17 00:00:00 2001 From: Vincent Clipet Date: Mon, 10 Mar 2025 16:49:42 +0100 Subject: [PATCH 23/33] Fixed missing closing parenthesis in 2-ui/4-forms-control/1-form-elements/article.md --- 2-ui/4-forms-controls/1-form-elements/article.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/2-ui/4-forms-controls/1-form-elements/article.md b/2-ui/4-forms-controls/1-form-elements/article.md index f22518d9d..7bc87a0f0 100644 --- a/2-ui/4-forms-controls/1-form-elements/article.md +++ b/2-ui/4-forms-controls/1-form-elements/article.md @@ -244,7 +244,7 @@ This syntax is optional. We can use `document.createElement('option')` and set a - `defaultSelected` -- if `true`, then `selected` HTML-attribute is created, - `selected` -- if `true`, then the option is selected. -The difference between `defaultSelected` and `selected` is that `defaultSelected` sets the HTML-attribute (that we can get using `option.getAttribute('selected')`, while `selected` sets whether the option is selected or not. +The difference between `defaultSelected` and `selected` is that `defaultSelected` sets the HTML-attribute (that we can get using `option.getAttribute('selected')`), while `selected` sets whether the option is selected or not. In practice, one should usually set _both_ values to `true` or `false`. (Or, simply omit them; both default to `false`.) From 3de63df651b3b796da5b2f2d3c21f9bd20cfaf28 Mon Sep 17 00:00:00 2001 From: Ilya Kantor Date: Mon, 24 Mar 2025 23:30:16 +0100 Subject: [PATCH 24/33] promise.all task --- .../04-promise-all-failure/solution.md | 85 +++++++++++++++++++ .../04-promise-all-failure/task.md | 76 +++++++++++++++++ 2 files changed, 161 insertions(+) create mode 100644 1-js/11-async/08-async-await/04-promise-all-failure/solution.md create mode 100644 1-js/11-async/08-async-await/04-promise-all-failure/task.md diff --git a/1-js/11-async/08-async-await/04-promise-all-failure/solution.md b/1-js/11-async/08-async-await/04-promise-all-failure/solution.md new file mode 100644 index 000000000..ed6a6c922 --- /dev/null +++ b/1-js/11-async/08-async-await/04-promise-all-failure/solution.md @@ -0,0 +1,85 @@ + +The problem is that `Promise.all` immediately rejects when one of its promises rejects. In our case, the second query fails, so `Promise.all` rejects, and the `try...catch` block catches this error. + +Meanwhile, even if one of the queries fails, other promises are *not affected* - they independently continue their execution. In our case, the third query throws an error of its own after a bit of time. And that error is never caught. We can see it in the console. + +The problem is especially dangerous in server-side environments, such as Node.js, when an uncaught error may cause the process to crash. + +How to fix it? + +A natural solution would be to cancel all unfinished queries when one of them fails. This way we avoid any potential errors. + +However, the bad news is that service calls (such as `database.query`) are often implemented by a 3rd-party library which doesn't support cancellation. So there's usually no way to cancel a call. + +Instead we can write our own wrapper function around `Promise.all` which adds a custom `then/catch` handler to each promise to track them: results are gathered and, if an error occurs, all subsequent promises are ignored. + +```js +function customPromiseAll(promises) { + return new Promise((resolve, reject) => { + const results = []; + let resultsCount = 0; + let hasError = false; // we'll set it to true upon first error + + promises.forEach((promise, index) => { + promise + .then(result => { + if (hasError) return; // ignore the promise if already errored + results[index] = result; + resultsCount++; + if (resultsCount === promises.length) { + resolve(results); // when all results are ready - successs + } + }) + .catch(error => { + if (hasError) return; // ignore the promise if already errored + hasError = true; // wops, error! + reject(error); // fail with rejection + }); + }); + }); +} +``` + +This approach has an issue of its own - it's often undesirable to `disconnect()` when queries are still in the process. + +It may be important that all queries complete, especially if some of them make important updates. + +So we should wait until all promises are settled before going further with the execution and eventually disconnecting. + +Here's one more implementation. It also resolves with the first error, but waits until all promises are settled. + +```js +function customPromiseAllWait(promises) { + return new Promise((resolve, reject) => { + const results = new Array(promises.length); + let settledCount = 0; + let firstError = null; + + promises.forEach((promise, index) => { + Promise.resolve(promise) + .then(result => { + results[index] = result; + }) + .catch(error => { + if (firstError === null) { + firstError = error; + } + }) + .finally(() => { + settledCount++; + if (settledCount === promises.length) { + if (firstError !== null) { + reject(firstError); + } else { + resolve(results); + } + } + }); + }); + }); +} +``` + +Now `await customPromiseAllWait(...)` will stall the execution until all queries are processed. + +This is the most reliable approach. diff --git a/1-js/11-async/08-async-await/04-promise-all-failure/task.md b/1-js/11-async/08-async-await/04-promise-all-failure/task.md new file mode 100644 index 000000000..2b8dc4aa5 --- /dev/null +++ b/1-js/11-async/08-async-await/04-promise-all-failure/task.md @@ -0,0 +1,76 @@ + +# Dangerous Promise.all + +`Promise.all` is a great way to parallelize multiple operations. It's especially useful when we need to make parallel requests to multiple services. + +However, there's a hidden danger. Hopefully we'll be able to identify its cause. + +Let's say we have a connection to a remote service, such as a database. + +There're two functions: `connect()` and `disconnect()`. + +When connected, we can send requests using `database.query(...)` - an async function which usually returns the result but also may throw an error. + +Here's a simple implementation: + +```js +let database; + +function connect() { + database = { + async query(isOk) { + if (!isOk) throw new Error('Query failed'); + } + }; +} + +function disconnect() { + database = null; +} + +// intended usage: +// connect() +// ... +// database.query(true) to emulate a successful call +// database.query(false) to emulate a failed call +// ... +// disconnect() +``` + +Now here's the problem. + +We write a simple code to connect and send 3 queries in parallel (all of them take different time, e.g. 100, 200 and 300ms), then disconnect: + +```js +// Helper function to call async function fn after ms milliseconds +function delay(fn, ms) { + return new Promise((resolve, reject) => { + setTimeout(() => fn().then(resolve, reject), ms); + }); +} + +async function run() { + connect(); + + try { + await Promise.all([ + // these 3 parallel jobs take different time: 100, 200 and 300 ms + delay(() => database.query(true), 100), + delay(() => database.query(false), 200), + delay(() => database.query(false), 300) + ]); + } catch(error) { + console.log('Error handled (or was it?)'); + } + + disconnect(); +} + +run(); +``` + +Two of these queries are (by chance) unsuccessful, but we're smart enough to wrap the `Promise.all` call into a `try...catch` block. + +However, this script actually leads to an uncaught error in console! + +Why? How to avoid it? \ No newline at end of file From ef31066fc570fc4528c55275c951342ba1fda7d8 Mon Sep 17 00:00:00 2001 From: Ilya Kantor Date: Mon, 24 Mar 2025 23:50:06 +0100 Subject: [PATCH 25/33] minor fixes --- .../08-async-await/04-promise-all-failure/task.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/1-js/11-async/08-async-await/04-promise-all-failure/task.md b/1-js/11-async/08-async-await/04-promise-all-failure/task.md index 2b8dc4aa5..5e64f96fa 100644 --- a/1-js/11-async/08-async-await/04-promise-all-failure/task.md +++ b/1-js/11-async/08-async-await/04-promise-all-failure/task.md @@ -3,7 +3,7 @@ `Promise.all` is a great way to parallelize multiple operations. It's especially useful when we need to make parallel requests to multiple services. -However, there's a hidden danger. Hopefully we'll be able to identify its cause. +However, there's a hidden danger. We'll see an example in this task and explore how to avoid it. Let's say we have a connection to a remote service, such as a database. @@ -39,7 +39,7 @@ function disconnect() { Now here's the problem. -We write a simple code to connect and send 3 queries in parallel (all of them take different time, e.g. 100, 200 and 300ms), then disconnect: +We wrote the code to connect and send 3 queries in parallel (all of them take different time, e.g. 100, 200 and 300ms), then disconnect: ```js // Helper function to call async function fn after ms milliseconds @@ -69,8 +69,8 @@ async function run() { run(); ``` -Two of these queries are (by chance) unsuccessful, but we're smart enough to wrap the `Promise.all` call into a `try...catch` block. +Two of these queries are (by chance) unsuccessful, but we handle it by wrapping the `Promise.all` call into a `try..catch` block. -However, this script actually leads to an uncaught error in console! +However, this doesn't help! This script actually leads to an uncaught error in console! Why? How to avoid it? \ No newline at end of file From de4247b8ba964f497875aab256f8d9d797b5d91d Mon Sep 17 00:00:00 2001 From: Ilya Kantor Date: Mon, 24 Mar 2025 23:51:08 +0100 Subject: [PATCH 26/33] minor fixes --- 1-js/11-async/08-async-await/04-promise-all-failure/task.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/1-js/11-async/08-async-await/04-promise-all-failure/task.md b/1-js/11-async/08-async-await/04-promise-all-failure/task.md index 5e64f96fa..a90160d78 100644 --- a/1-js/11-async/08-async-await/04-promise-all-failure/task.md +++ b/1-js/11-async/08-async-await/04-promise-all-failure/task.md @@ -55,9 +55,12 @@ async function run() { try { await Promise.all([ // these 3 parallel jobs take different time: 100, 200 and 300 ms + // we use delay helper for this effect +*!* delay(() => database.query(true), 100), delay(() => database.query(false), 200), delay(() => database.query(false), 300) +*/!* ]); } catch(error) { console.log('Error handled (or was it?)'); From 0af25bca8a7b2ff5e4f97ef76afc6d7ae4c5567b Mon Sep 17 00:00:00 2001 From: Ilya Kantor Date: Mon, 24 Mar 2025 23:52:07 +0100 Subject: [PATCH 27/33] minor fixes --- 1-js/11-async/08-async-await/04-promise-all-failure/task.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/1-js/11-async/08-async-await/04-promise-all-failure/task.md b/1-js/11-async/08-async-await/04-promise-all-failure/task.md index a90160d78..d6fc30e50 100644 --- a/1-js/11-async/08-async-await/04-promise-all-failure/task.md +++ b/1-js/11-async/08-async-await/04-promise-all-failure/task.md @@ -55,7 +55,7 @@ async function run() { try { await Promise.all([ // these 3 parallel jobs take different time: 100, 200 and 300 ms - // we use delay helper for this effect + // we use delay helper to achieve this effect *!* delay(() => database.query(true), 100), delay(() => database.query(false), 200), @@ -72,7 +72,7 @@ async function run() { run(); ``` -Two of these queries are (by chance) unsuccessful, but we handle it by wrapping the `Promise.all` call into a `try..catch` block. +Two of these queries happen to be unsuccessful, but we're smart enough to wrap the `Promise.all` call into a `try..catch` block. However, this doesn't help! This script actually leads to an uncaught error in console! From d932e522d6e483d1bd639bca6796f215dd34d9dd Mon Sep 17 00:00:00 2001 From: Ilya Kantor Date: Mon, 24 Mar 2025 23:52:42 +0100 Subject: [PATCH 28/33] minor fixes --- 1-js/11-async/08-async-await/04-promise-all-failure/task.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/1-js/11-async/08-async-await/04-promise-all-failure/task.md b/1-js/11-async/08-async-await/04-promise-all-failure/task.md index d6fc30e50..74571c43e 100644 --- a/1-js/11-async/08-async-await/04-promise-all-failure/task.md +++ b/1-js/11-async/08-async-await/04-promise-all-failure/task.md @@ -42,7 +42,7 @@ Now here's the problem. We wrote the code to connect and send 3 queries in parallel (all of them take different time, e.g. 100, 200 and 300ms), then disconnect: ```js -// Helper function to call async function fn after ms milliseconds +// Helper function to call async function `fn` after `ms` milliseconds function delay(fn, ms) { return new Promise((resolve, reject) => { setTimeout(() => fn().then(resolve, reject), ms); @@ -55,7 +55,7 @@ async function run() { try { await Promise.all([ // these 3 parallel jobs take different time: 100, 200 and 300 ms - // we use delay helper to achieve this effect + // we use the `delay` helper to achieve this effect *!* delay(() => database.query(true), 100), delay(() => database.query(false), 200), From f0d8abbc136dcad9f9424d6b69e75dc87a393586 Mon Sep 17 00:00:00 2001 From: Ilya Kantor Date: Mon, 24 Mar 2025 23:54:09 +0100 Subject: [PATCH 29/33] minor fixes --- .../08-async-await/04-promise-all-failure/solution.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/1-js/11-async/08-async-await/04-promise-all-failure/solution.md b/1-js/11-async/08-async-await/04-promise-all-failure/solution.md index ed6a6c922..21e469c18 100644 --- a/1-js/11-async/08-async-await/04-promise-all-failure/solution.md +++ b/1-js/11-async/08-async-await/04-promise-all-failure/solution.md @@ -1,7 +1,7 @@ -The problem is that `Promise.all` immediately rejects when one of its promises rejects. In our case, the second query fails, so `Promise.all` rejects, and the `try...catch` block catches this error. +The root of the problem is that `Promise.all` immediately rejects when one of its promises rejects, but it do nothing to cancel the other promises. -Meanwhile, even if one of the queries fails, other promises are *not affected* - they independently continue their execution. In our case, the third query throws an error of its own after a bit of time. And that error is never caught. We can see it in the console. +In our case, the second query fails, so `Promise.all` rejects, and the `try...catch` block catches this error.Meanwhile, other promises are *not affected* - they independently continue their execution. In our case, the third query throws an error of its own after a bit of time. And that error is never caught, we can see it in the console. The problem is especially dangerous in server-side environments, such as Node.js, when an uncaught error may cause the process to crash. From f7758353711601d5a17d50ef1676720beff6eaf4 Mon Sep 17 00:00:00 2001 From: Ilya Kantor Date: Mon, 24 Mar 2025 23:55:01 +0100 Subject: [PATCH 30/33] minor fixes --- .../08-async-await/04-promise-all-failure/solution.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/1-js/11-async/08-async-await/04-promise-all-failure/solution.md b/1-js/11-async/08-async-await/04-promise-all-failure/solution.md index 21e469c18..b340b0996 100644 --- a/1-js/11-async/08-async-await/04-promise-all-failure/solution.md +++ b/1-js/11-async/08-async-await/04-promise-all-failure/solution.md @@ -7,9 +7,9 @@ The problem is especially dangerous in server-side environments, such as Node.js How to fix it? -A natural solution would be to cancel all unfinished queries when one of them fails. This way we avoid any potential errors. +An ideal solution would be to cancel all unfinished queries when one of them fails. This way we avoid any potential errors. -However, the bad news is that service calls (such as `database.query`) are often implemented by a 3rd-party library which doesn't support cancellation. So there's usually no way to cancel a call. +However, the bad news is that service calls (such as `database.query`) are often implemented by a 3rd-party library which doesn't support cancellation. Then there's no way to cancel a call. Instead we can write our own wrapper function around `Promise.all` which adds a custom `then/catch` handler to each promise to track them: results are gathered and, if an error occurs, all subsequent promises are ignored. From 0760c909d345738de0a6bf322d36337746253c27 Mon Sep 17 00:00:00 2001 From: Ilya Kantor Date: Mon, 24 Mar 2025 23:55:35 +0100 Subject: [PATCH 31/33] minor fixes --- .../08-async-await/04-promise-all-failure/solution.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/1-js/11-async/08-async-await/04-promise-all-failure/solution.md b/1-js/11-async/08-async-await/04-promise-all-failure/solution.md index b340b0996..8a114c96c 100644 --- a/1-js/11-async/08-async-await/04-promise-all-failure/solution.md +++ b/1-js/11-async/08-async-await/04-promise-all-failure/solution.md @@ -11,7 +11,7 @@ An ideal solution would be to cancel all unfinished queries when one of them fai However, the bad news is that service calls (such as `database.query`) are often implemented by a 3rd-party library which doesn't support cancellation. Then there's no way to cancel a call. -Instead we can write our own wrapper function around `Promise.all` which adds a custom `then/catch` handler to each promise to track them: results are gathered and, if an error occurs, all subsequent promises are ignored. +As an alternative, we can write our own wrapper function around `Promise.all` which adds a custom `then/catch` handler to each promise to track them: results are gathered and, if an error occurs, all subsequent promises are ignored. ```js function customPromiseAll(promises) { @@ -46,7 +46,7 @@ It may be important that all queries complete, especially if some of them make i So we should wait until all promises are settled before going further with the execution and eventually disconnecting. -Here's one more implementation. It also resolves with the first error, but waits until all promises are settled. +Here's another implementation. It also resolves with the first error, but waits until all promises are settled. ```js function customPromiseAllWait(promises) { From 5dea44151bdd7791380b65d8b8bb60779f2576ca Mon Sep 17 00:00:00 2001 From: Ilya Kantor Date: Mon, 24 Mar 2025 23:58:32 +0100 Subject: [PATCH 32/33] minor fixes --- .../08-async-await/04-promise-all-failure/solution.md | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/1-js/11-async/08-async-await/04-promise-all-failure/solution.md b/1-js/11-async/08-async-await/04-promise-all-failure/solution.md index 8a114c96c..5689432d2 100644 --- a/1-js/11-async/08-async-await/04-promise-all-failure/solution.md +++ b/1-js/11-async/08-async-await/04-promise-all-failure/solution.md @@ -46,7 +46,7 @@ It may be important that all queries complete, especially if some of them make i So we should wait until all promises are settled before going further with the execution and eventually disconnecting. -Here's another implementation. It also resolves with the first error, but waits until all promises are settled. +Here's another implementation. It behaves similar to `Promise.all` - also resolves with the first error, but waits until all promises are settled. ```js function customPromiseAllWait(promises) { @@ -82,4 +82,7 @@ function customPromiseAllWait(promises) { Now `await customPromiseAllWait(...)` will stall the execution until all queries are processed. -This is the most reliable approach. +This is a more reliable approach. + +Lastly, if we'd like to know about all the errors, e.g. for logging purposes, we can use `Promise.allSettled`. + From 035c5267ba80fa7b55878f7213cbde449b4092d9 Mon Sep 17 00:00:00 2001 From: Ilya Kantor Date: Tue, 25 Mar 2025 00:02:18 +0100 Subject: [PATCH 33/33] minor fixes --- .../04-promise-all-failure/solution.md | 29 +++++++++++++++++-- 1 file changed, 27 insertions(+), 2 deletions(-) diff --git a/1-js/11-async/08-async-await/04-promise-all-failure/solution.md b/1-js/11-async/08-async-await/04-promise-all-failure/solution.md index 5689432d2..9fda8e000 100644 --- a/1-js/11-async/08-async-await/04-promise-all-failure/solution.md +++ b/1-js/11-async/08-async-await/04-promise-all-failure/solution.md @@ -82,7 +82,32 @@ function customPromiseAllWait(promises) { Now `await customPromiseAllWait(...)` will stall the execution until all queries are processed. -This is a more reliable approach. +This is a more reliable approach, as it guarantees a predictable execution flow. -Lastly, if we'd like to know about all the errors, e.g. for logging purposes, we can use `Promise.allSettled`. +Lastly, if we'd like to process all errors, we can use either use `Promise.allSettled` or write a wrapper around it to gathers all errors in a single [AggregateError](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/AggregateError) object and rejects with it. +```js +// wait for all promises to settle +// return results if no errors +// throw AggregateError with all errors if any +function allOrAggregateError(promises) { + return Promise.allSettled(promises).then(results => { + const errors = []; + const values = []; + + results.forEach((res, i) => { + if (res.status === 'fulfilled') { + values[i] = res.value; + } else { + errors.push(res.reason); + } + }); + + if (errors.length > 0) { + throw new AggregateError(errors, 'One or more promises failed'); + } + + return values; + }); +} +``` pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy