diff --git a/1-js/02-first-steps/14-function-basics/2-rewrite-function-question-or/task.md b/1-js/02-first-steps/14-function-basics/2-rewrite-function-question-or/task.md index 5e0c56ad..f70ad6fa 100644 --- a/1-js/02-first-steps/14-function-basics/2-rewrite-function-question-or/task.md +++ b/1-js/02-first-steps/14-function-basics/2-rewrite-function-question-or/task.md @@ -13,7 +13,7 @@ function checkAge(age) { if (age > 18) { return true; } else { - return confirm('Did parents allow you?'); + return confirm('Do you have your parents permission to access this page?'); } } ``` @@ -24,4 +24,3 @@ Make two variants of `checkAge`: 1. Using a question mark operator `'?'` 2. Using OR `||` - diff --git a/2-ui/1-document/01-browser-environment/article.md b/2-ui/1-document/01-browser-environment/article.md index 58cc8e1e..5f6910dc 100644 --- a/2-ui/1-document/01-browser-environment/article.md +++ b/2-ui/1-document/01-browser-environment/article.md @@ -1,10 +1,10 @@ # Browser environment, specs -The JavaScript language was initially created for web browsers. But as of now, it evolved and became a language with many uses and platforms. +The JavaScript language was initially created for web browsers. Since then, it evolved and became a language with many uses and platforms. -A platform may be either a browser or a web-server or a washing machine or another *host*. Each of them provides platform-specific functionality. The JavaScript standard called that a *host environment*. +A platform may be either a browser or a web-server or a washing machine or another *host*. Each of them provides platform-specific functionality. The JavaScript specification calls that a *host environment*. -That host environment provides additional objects and functions to the language core. Web browsers provide means to control web pages. Node.JS provides server-side features. There are other host environments too. +A host environment provides platform-specific objects and functions additionally to the language core. Web browsers give means to control web pages. Node.JS provides server-side features, and so on. [cut] @@ -12,10 +12,10 @@ Here's a bird-eye view of what we have when JavaScript runs in a web-browser: ![](windowObjects.png) -The "root object" called `window` has two roles: +There's a "root" object called `window`. It has two roles: -1. First, it is a [global object](info:global-object) for JavaScript code. -2. Second, it represents the "browser window" object, provides methods to control it. +1. First, it is a global object for JavaScript code, as described in the chapter . +2. Second, it represents the "browser window" and provides methods to control it. For instance, here we use it as a global object: @@ -24,18 +24,21 @@ function sayHi() { alert("Hello"); } -alert(window.sayHi); // global function is a property of window +// global functions are accessible as properties of window +alert(window.sayHi); ``` And here we use it as a browser window, to see the window height: ```js run -alert(window.innerHeight); // some number +alert(window.innerHeight); // inner window height ``` +There are more window-specific methods and properties, we'll cover them later. + ## Document Object Model (DOM) -The `document` object gives access to the page content. We can change or create literally anything. +The `document` object gives access to the page content. We can change or create anything on the page using it. For instance: ```js run @@ -46,24 +49,20 @@ document.body.style.background = 'red'; setTimeout(() => document.body.style.background = '', 1000); ``` -Here we used `document.body.style`, but there's much, much more. Properties and methods are described in the specification. - -There are two working groups who develop it: +Here we used `document.body.style`, but there's much, much more. Properties and methods are described in the specification. By chance, there are two working groups who develop it: 1. [W3C](https://en.wikipedia.org/wiki/World_Wide_Web_Consortium) -- the documentation is at . 2. [WhatWG](https://en.wikipedia.org/wiki/WHATWG), publishing at . -As it happens, the two groups don't always agree, so we have like 2 sets of standards. But they are in tight contact and eventually things merge. So the documentation that you can find on the given resources is very similar, like 99%. There are very minor differences, but you probably won't notice them. +As it happens, the two groups don't always agree, so we have like 2 sets of standards. But they are in the tight contact and eventually things merge. So the documentation that you can find on the given resources is very similar, there's like 99% match. There are very minor differences, you probably won't notice them. -I find more pleasant to use, and so recommend it. +Personally, I find more pleasant to use. -In the ancient past, once there was no standard at all -- each browser did whatever it wanted. So different browsers had different sets methods and properties for the same thing, and developers had to write different code for each of them. Dark times indeed. +In the ancient past, there was no standard at all -- each browser implemented whatever it wanted. So different browsers had different sets methods and properties for the same thing, and developers had to write different code for each of them. Dark, messy times. Even now we can sometimes meet old code that uses browser-specific properties and works around incompatibilities. But in this tutorial we'll use modern stuff: there's no need to learn old things until you really need those (chances are high you won't). -Then the DOM standard appeared, in an attempt to bring everyone to an agreement. The first version was DOM Level 1, then it was extended by DOM Level 2, then DOM Level 3, and now it's DOM Level 4. - -People from WhatWG group got tired of version and are calling that just "DOM", without a number. So will do we. +Then the DOM standard appeared, in an attempt to bring everyone to an agreement. The first version was "DOM Level 1", then it was extended by DOM Level 2, then DOM Level 3, and now it's DOM Level 4. People from WhatWG group got tired of version and are calling that just "DOM", without a number. So will do we. ```smart header="DOM is not only for browsers" The DOM specification explains the structure of a document and provides objects to manipulate it. There are non-browser instruments that use it too. @@ -72,9 +71,9 @@ For instance, server-side tools that download HTML pages and process them. They ``` ```smart header="CSSOM for styling" -CSS styles and stylesheets are structured not like HTML. So there's a separate specification [CSSOM](https://www.w3.org/TR/cssom-1/) that explains how CSS styles and rules can be represented as objects, how to read and write them. +CSS rules and stylesheets are structured not like HTML. So there's a separate specification [CSSOM](https://www.w3.org/TR/cssom-1/) that explains how they are represented as objects, how to read and write them. -Usually we take a style somewhere from the document and apply it to the document, so CSSOM is used together with DOM. But CSSOM is applied not often, because we rarely need to modify CSS rules from JavaScript, so we won't cover it right now. +CSSOM is used together with DOM when we modify style rules for the document. In practice though, CSSOM is rarely required, because usually CSS rules are static. We rarely need to add/remove CSS rules from JavaScript, so we won't cover it right now. ``` ## BOM (part of HTML spec) @@ -95,7 +94,7 @@ if (confirm("Go to wikipedia?")) { } ``` -Functions `alert/confirm/prompt` -- are also a part of BOM: they are directly not related to the document, but represent pure browser methods of communicating with the user. +Functions `alert/confirm/prompt` are also a part of BOM: they are directly not related to the document, but represent pure browser methods of communicating with the user. ```smart header="HTML specification" @@ -112,11 +111,13 @@ DOM specification : Describes the document structure, manipulations and events, see . CSSOM specification -: Describes styles, manipulations with them and their binding to documents, see . +: Describes stylesheets and style rules, manipulations with them and their binding to documents, see . HTML specification : Describes HTML language (tags etc) and also BOM (browser object model) -- various browser functions: `setTimeout`, `alert`, `location` and so on, see . It takes DOM specification and extends it with many additional properties and methods. Now we'll get down to learning DOM, because the document plays the central role in the UI, and working with it is the most complex part. -Please note the links above, because there's so many stuff to learn, it's impossible to cover and remember everything. When you'd like to read about a property or a method -- the Mozilla manual at is a nice one, but reading the corresponding spec may be better. More complex and longer to read, but will make your fundamental knowledge sound and complete. +Please note the links above, because there's so many stuff to learn, it's impossible to cover and remember everything. + +When you'd like to read about a property or a method -- the Mozilla manual at is a nice one, but reading the corresponding spec may be better: more complex and longer to read, but will make your fundamental knowledge sound and complete. diff --git a/2-ui/1-document/02-dom-nodes/article.md b/2-ui/1-document/02-dom-nodes/article.md index 7234e9a6..635986d1 100644 --- a/2-ui/1-document/02-dom-nodes/article.md +++ b/2-ui/1-document/02-dom-nodes/article.md @@ -6,9 +6,13 @@ libs: # DOM tree -The essential part of HTML is tags, right? +The backbone of an HTML document is tags. -According to Document Object Model (DOM), every HTML-tag is an object. Nested tags are called "children". And the text inside it is an object as well. All these objects are accessible using JavaScript, we'll see that now. +According to Document Object Model (DOM), every HTML-tag is an object. Nested tags are called "children" of the enclosing one. + +The text inside a tag it is an object as well. + +All these objects are accessible using JavaScript. ## An example of DOM diff --git a/5-animation/2-css-animations/3-animate-circle/solution.md b/5-animation/2-css-animations/3-animate-circle/solution.md new file mode 100644 index 00000000..e69de29b diff --git a/5-animation/2-css-animations/3-animate-circle/solution.view/index.html b/5-animation/2-css-animations/3-animate-circle/solution.view/index.html new file mode 100644 index 00000000..88b15425 --- /dev/null +++ b/5-animation/2-css-animations/3-animate-circle/solution.view/index.html @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + + + diff --git a/5-animation/2-css-animations/3-animate-circle/source.view/index.html b/5-animation/2-css-animations/3-animate-circle/source.view/index.html new file mode 100644 index 00000000..a423280d --- /dev/null +++ b/5-animation/2-css-animations/3-animate-circle/source.view/index.html @@ -0,0 +1,28 @@ + + + + + + + + + + +
+ + + diff --git a/5-animation/2-css-animations/3-animate-circle/task.md b/5-animation/2-css-animations/3-animate-circle/task.md new file mode 100644 index 00000000..83bbb3e8 --- /dev/null +++ b/5-animation/2-css-animations/3-animate-circle/task.md @@ -0,0 +1,16 @@ +importance: 5 + +--- + +# Animated circle + +Create a function `showCircle(cx, cy, radius)` that shows an animated growing circle. + +- `cx,cy` are window-relative coordinates of the center of the circle, +- `radius` is the radius of the circle. + +Click the button below to see how it should look like: + +[iframe src="solution" height=260] + +The source document has an example of a circle with right styles, so the task is precisely to do the animation right. diff --git a/8-async/01-callback-hell/01-animate-circle-callback/solution.md b/8-async/01-callback-hell/01-animate-circle-callback/solution.md new file mode 100644 index 00000000..e69de29b diff --git a/8-async/01-callback-hell/01-animate-circle-callback/solution.view/index.html b/8-async/01-callback-hell/01-animate-circle-callback/solution.view/index.html new file mode 100644 index 00000000..b2192681 --- /dev/null +++ b/8-async/01-callback-hell/01-animate-circle-callback/solution.view/index.html @@ -0,0 +1,59 @@ + + + + + + + + + + + + + + + + + diff --git a/8-async/01-callback-hell/01-animate-circle-callback/task.md b/8-async/01-callback-hell/01-animate-circle-callback/task.md new file mode 100644 index 00000000..4a20ca60 --- /dev/null +++ b/8-async/01-callback-hell/01-animate-circle-callback/task.md @@ -0,0 +1,25 @@ + +# Animated circle with callback + +In the task an animated growing circle is shown. + +Now let's say we need not just a circle, but to show a message inside it. The message should appear *after* the animation is complete (the circle is fully grown), otherwise it would look ugly. + +In the solution of the task, the function `showCircle(cx, cy, radius)` draws the circle, but gives no way to track when it's ready. + +Add a callback argument: `showCircle(cx, cy, radius, callback)` to be called when the animation is complete. The `callback` should receive the circle `
` as an argument. + +Here's the example: + +```js +showCircle(150, 150, 100, div => { + div.classList.add('message-ball'); + div.append("Hello, world!"); +}); +``` + +Demo: + +[iframe src="solution" height=260] + +Take the solution of the task as the base. diff --git a/8-async/01-callback-hell/article.md b/8-async/01-callback-hell/article.md index c79d0109..293cf90a 100644 --- a/8-async/01-callback-hell/article.md +++ b/8-async/01-callback-hell/article.md @@ -1,13 +1,6 @@ # Callback hell -Many things that we do in JavaScript are asynchronous. We initiate a process, but it finishes later. - -The most obvious example is `setTimeout`, but there are others, like making network requests, performing animations and so on. - -[cut] - -## Callbacks Consider this function `loadScript(src)` that loads a script: @@ -28,19 +21,28 @@ We can use it like this: loadScript('/my/script.js'); ``` -The function is asynchronous: the script starts loading now, but finishes later. +The function is *asynchronous*: the script starts loading now, but finishes later. -```smart header="Synchronous vs asynchronous" "Synchonous" and "asynchronous" are general programming terms, not specific to JavaScript. -A *synchronous* action suspends the execution until it's completed. For instance, `alert` and `prompt` are synchronous: the program may not continue until they are finished. +A *synchronous* action suspends the execution until it's completed. For instance, a call to `alert` or `prompt` is synchronous: the program may not continue until it's finished. -An *asynchronous* action allows the program to continue while it's in progress. For instance, `loadScript` in the example above initiates the script loading, but does not suspend the execution. Other commands may execute while the script is loading. +```js +let age = prompt("How old are you", 20); +// the execution of the code below awaits for the prompt to finish ``` -As of now, `loadScript` provides no way to track the load end. How can we execute our own code after the script is loaded? +An *asynchronous* action allows the program to continue while it's in progress. For instance, a call to `loadScript` is asynchronous. It initiates the script loading, but does not suspend the execution. Other commands may execute while the script is loading: -Let's allow that by adding a custom function as a second argument to `loadScript`, that should execute at that moment: +```js +loadScript('/my/script.js'); +// the execution does not wait for the script loading to finish, +// it just goes on +``` + +As of now, `loadScript` provides no way to track the load completion. The script loads and eventually runs. + +Let's add a `callback` function as a second argument to `loadScript`, that should execute at when the script is loaded. ```js function loadScript(src, callback) { @@ -53,15 +55,25 @@ function loadScript(src, callback) { } ``` -Now when we want to load a script and then do something, we can call: +Now we're able to load a script and run our code that can use new functions from it, like here: -```js -loadScript('/my/script.js', function(script) { - alert(`Cool, the ${script.src} is loaded, let's use it`); +```js run +function loadScript(src, callback) { + let script = document.createElement('script'); + script.src = src; + script.onload = () => callback(script); + document.head.append(script); +} + +*!* +loadScript('https://cdnjs.cloudflare.com/ajax/libs/lodash.js/3.2.0/lodash.js', function(script) { + alert(`Cool, the ${script.src} is loaded`); + alert( _ ); // function declared in the loaded script }); +*/!* ``` -...And it works, shows the `alert` after the script is loaded. +That's called a "callback style" of asynchronous programming. A function that does something asynchronously provides a callback argument where we put the code to run after it's complete. ## Callback in callback @@ -92,24 +104,22 @@ loadScript('/my/script.js', function(script) { loadScript('/my/script2.js', function(script) { - if (something) { *!* - loadScript('/my/script3.js', function(script) { - // ...continue after all scripts are loaded - }); + loadScript('/my/script3.js', function(script) { + // ...continue after all scripts are loaded + }); */!* - } }) }); ``` -As you can see, a new asynchronous action means one more nesting level. +As we can see, a new asynchronous action means one more nesting level. So the code becomes deeper and deeper. ## Handling errors -In this example we didn't consider errors. What if a script loading failed with an error? Our callback should be able to react on that. +In examples above we didn't consider errors. What if a script loading failed with an error? Our callback should be able to react on that. Here's an improved version of `loadScript` that tracks loading errors: @@ -140,12 +150,14 @@ loadScript('/my/script.js', function(error, script) { }); ``` -The first argument of `callback` is reserved for errors, and the second argument is for the successful result. +The convention is: +1. The first argument of `callback` is reserved for an error if it occurs. +2. The second argument (and successive ones if needed) are for the successful result. + +So the single `callback` function is used both for reporting errors and passing back results. ## Pyramid of doom -What we've just seen is called a "callback-based" approach to asynchronous programming. We pass a function, and it should run after the process is complete: with an error or a successful result. - From the first look it's a viable way of asynchronous coding. And indeed it is. For one or maybe two nested calls it looks fine. But for multiple asynchronous actions that follow one after another we'll have a code like this: @@ -191,4 +203,38 @@ That's sometimes called "callback hell" or "pyramid of doom". The pyramid grows to the right with every asynchronous action. Soon it spirales out of control. -Fortunately, there are ways to evade such pyramids. One of them is using "promises", we'll study them in the next chapters. +In simple cases we can evade the problem by making every action a standalone function, like this: + +```js +loadScript('1.js', step1); + +function step1(error, script) { + if (error) { + handleError(error); + } else { + // ... + loadScript('2.js', step2); + } +} + +function step2(error, script) { + if (error) { + handleError(error); + } else { + // ... + loadScript('3.js', step3); + } +} + +function step3(error, script) { + if (error) { + handleError(error); + } else { + // ...continue after all scripts are loaded (*) + } +}; +``` + +See? There's no deep nesting now, because we moved every function to the top. But the code looks like a torn apart spreadsheet. We need to eye-jump between pieces while reading it. The functions `step*` have no use, they are only created to evade the "pyramid of doom". + +Luckily, there are other ways to evade such pyramids. Most modern code makes use of "promises", we'll study them in the next chapter. diff --git a/8-async/01-callback-hell/one.js b/8-async/01-callback-hell/one.js new file mode 100644 index 00000000..948a60e0 --- /dev/null +++ b/8-async/01-callback-hell/one.js @@ -0,0 +1,3 @@ +function one() { + alert(1); +} diff --git a/8-async/02-promise-basics/article.md b/8-async/02-promise-basics/article.md index d20c9f99..0dd2e548 100644 --- a/8-async/02-promise-basics/article.md +++ b/8-async/02-promise-basics/article.md @@ -2,6 +2,13 @@ A promise is an object of the built-in `Promise` class. It has the meaning of the "delayed result". +The promise object has two inernal fields in it: + +- `state` -- one of: "pending", "fulfilled", "rejected". +- `result` -- when `new Promise` is created. + + + The constructor syntax is: ```js diff --git a/8-async/03-promise-chaining/article.md b/8-async/03-promise-chaining/article.md index f08145d8..dd29a919 100644 --- a/8-async/03-promise-chaining/article.md +++ b/8-async/03-promise-chaining/article.md @@ -253,7 +253,7 @@ new Promise(function(resolve, reject) { ``` -### Rethrowing +## Rethrowing As we already noticed, `.catch` is like `try..catch`. We may have as many `.then` as we want, and then use a single `.catch` at the end to handle errors in all of them. @@ -308,7 +308,7 @@ new Promise(function(resolve, reject) { }).then(alert); // undefined ``` -### Unhandled rejections +## Unhandled rejections What if we forget to handle an error? @@ -343,6 +343,7 @@ Usually that means that the code is bad. Most JavaScript engines track such situ // open in a new window to see in action window.addEventListener('unhandledrejection', function(event) { + // the event object has two special properties: alert(event.promise); // the promise that generated the error alert(event.reason); // the error itself (Whoops!) }); @@ -359,53 +360,3 @@ new Promise(function() { ``` In non-browser environments there's also a similar event, so we can always track unhandled errors in promises. - - -An object that has a method called `.then` is called a "thenable". - -Instead of checking if something is `instanceof Promise`, we should usually check it for being thenable, and if it is, then treat it as a promise ("duck typing"). - -JavaScript specification also checks the value returned by a handler for being a thenable, not exactly a promise, when it decides whether to pass it along the chain or wait for the result. So in the examples above we could use custom thenables instead of `Promise` instances. - -For instance, native promises give no way to "abort" the execution. The `loadScript` above cannot "cancel" script loading, just because there's no `.abort` method on promises, we can only listen for the state change using `.then/catch`. - -## Extending promises, thenables - -Promises are very simple by design. One of the thing they miss is the ability to cancel the process. - -For instance, `loadScript(src)` in previous examples returns a promise that allows to track success/failure of the loading. But can we abort it? No. - -We can inherit from `Promise` to introduce such functionality, like this: - - -// TODO: NOT WORKING AS INTENDED? - -```js run -function loadScript(src) { - let script = document.createElement('script'); - script.src = src; - - let promise = new Promise(function(resolve, reject) { - script.onload = () => resolve(script); -*!* - script.onerror = err => reject(new Error("Script load error: " + src)); // (*) -*/!* - }); - - document.head.append(script); - promise.abort = () => script.remove(); - return promise; -} - -let promise = loadScript("/article/promise-chaining/one.js"); -promise.then(alert); -promise.abort(); -``` - - - - - - - -## Inheriting from promise, thenables, promise api, async/await diff --git a/8-async/03-promise-chaining/one.js b/8-async/03-promise-chaining/one.js index 558a8d15..948a60e0 100644 --- a/8-async/03-promise-chaining/one.js +++ b/8-async/03-promise-chaining/one.js @@ -1 +1,3 @@ -let one = 1; +function one() { + alert(1); +} diff --git a/8-async/04-promise-api/article.md b/8-async/04-promise-api/article.md new file mode 100644 index 00000000..64f26397 --- /dev/null +++ b/8-async/04-promise-api/article.md @@ -0,0 +1,15 @@ +# Promises API + +Let's meet more functions and methods for promises. + + + +Keywords `async` and `await` provide a more elegant way to write the code using promises. + +## Async functions + +The `async` function is like a regular one, but it wraps a returned value in a `Promise`. + + + +Nowadays, promises are de-facto standard for asynchronous actions, when we need to diff --git a/8-async/05-async-await/article.md b/8-async/05-async-await/article.md new file mode 100644 index 00000000..52391aab --- /dev/null +++ b/8-async/05-async-await/article.md @@ -0,0 +1,11 @@ +# Async/await + +Keywords `async` and `await` provide a more elegant way to write the code using promises. + +## Async functions + +The `async` function is like a regular one, but it wraps a returned value in a `Promise`. + + + +Nowadays, promises are de-facto standard for asynchronous actions, when we need to diff --git a/8-async/whereto.txt b/8-async/whereto.txt new file mode 100644 index 00000000..2100b9b3 --- /dev/null +++ b/8-async/whereto.txt @@ -0,0 +1,58 @@ + +## Extending promises, thenables + +Promises are very simple by design. One of the thing they miss is the ability to cancel the process. + +For instance, `loadScript(src)` in previous examples returns a promise that allows to track success/failure of the loading. But can we abort it? No. + +We can inherit from `Promise` to introduce such functionality, like this: + + +```js run +function loadScript(src) { + let script = document.createElement('script'); + alert(1); + script.src = src; + + let promise = new Promise(function(resolve, reject) { + script.onload = () => { alert('onload'); resolve(script); } +*!* + script.onerror = err => reject(new Error("Script load error: " + src)); // (*) +*/!* + }); + + alert(2); + + document.head.append(script); + alert(3); + promise.abort = () => { + script.src = 'javascript:"" + '; + }; + return promise; +} + +let promise = loadScript("/article/promise-chaining/one.js?speed=0"); +promise.then(alert); +promise.abort(); +alert(4); +``` + + + + + + + +## Inheriting from promise, thenables, promise api, async/await + +-------- + + +An object that has a method called `.then` is called a "thenable". + +Instead of checking if something is `instanceof Promise`, we should usually check it for being thenable, and if it is, then treat it as a promise ("duck typing"). + +JavaScript specification also checks the value returned by a handler for being a thenable, not exactly a promise, when it decides whether to pass it along the chain or wait for the result. So in the examples above we could use custom thenables instead of `Promise` instances. + +For instance, native promises give no way to "abort" the execution. The `loadScript` above cannot "cancel" script loading, just because there's no `.abort` method on promises, we can only listen for the state change using `.then/catch`.