You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: chapters/ch09.asciidoc
+49-8Lines changed: 49 additions & 8 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -7,7 +7,7 @@ Reconciling all of these new features with our existing ES5 knowledge may seem l
7
7
8
8
We'll take a look at a few different features, the use cases where they shine, and the situations where we might be better off using features that were already available in the language. Let's go case by case.
9
9
10
-
==== 9.1 Variable Declarations
10
+
=== 9.1 Variable Declarations
11
11
12
12
When developing software, most of our time is spent reading code. ES6 offers +let+ and +const+ as new flavors of variable declaration, and part of the value in these statements is that they can signal how a variable is used. When reading a piece of code, others can take cues from these signals in order to better understand what we did. Cues like these are crucial to reducing the amount of time someone spends interpreting what a piece of code does, and as such we should try and leverage them whenever possible.
13
13
@@ -182,7 +182,7 @@ function getCompletionText(progress) {
182
182
}
183
183
----
184
184
185
-
==== 9.2 Template Literals
185
+
=== 9.2 Template Literals
186
186
187
187
For the longest time, JavaScript users have resorted to utility libraries to format strings, as that was never a part of the language until now. Creating a multi-line string was also a hassle, as was escaping single or double quotes -- depending on which quote style you were using. Template literals are different, and they fix all of these inconveniences.
188
188
@@ -361,7 +361,7 @@ npm run lint
361
361
362
362
In conclusion, there are trade-offs to consider when using template literals. You're invited to experiment with the backtick-first approach and gauge its merits. Always prefer convenience, over convention, over configuration.
363
363
364
-
==== 9.3 Shorthand Notation and Object Destructuring
364
+
=== 9.3 Shorthand Notation and Object Destructuring
365
365
366
366
Chapter 2 introduced us to the concept of shorthand notation. Whenever we want to introduce a property and there's a binding by the same name in scope, we can avoid repetition.
Destructuring is a great feature but it doesn't necessarily lead to more readable code every time. Use it judiciously, especially when there's not many host references being removed.
533
533
534
-
==== 9.4 Rest and Spread
534
+
=== 9.4 Rest and Spread
535
535
536
536
Matches for regular expressions are represented as an array. The matched portion of the input is placed in the first position, while each captured group is placed in subsequent elements in the array. Often, we are interested in specific captures such as the first one.
537
537
@@ -648,7 +648,7 @@ In the following bit of code, we map a list of persons to a list of person model
648
648
persons.map(({ name, ssn, ...person }) => person)
649
649
----
650
650
651
-
==== 9.5 Savoring Function Flavors
651
+
=== 9.5 Savoring Function Flavors
652
652
653
653
JavaScript already offered a number of ways in which we can declare functions before ES6.
654
654
@@ -801,7 +801,7 @@ function wait (delay) {
801
801
802
802
As a general rule of thumb, think of every function as a function declaration by default. If that function doesn't need a meaningful name, require several lines of code nor involve recursion, then consider an arrow function.
803
803
804
-
==== 9.6 Classes and Proxies
804
+
=== 9.6 Classes and Proxies
805
805
806
806
Most modern programming languages have classes in one form or another. JavaScript classes are syntactic sugar on top of prototypal inheritance. Using classes turns prototypes more idiomatic and easier for tools to statically analyze.
807
807
@@ -923,6 +923,47 @@ Abstractions are costly. Once an abstraction is in place, it is often hard to go
923
923
924
924
When we prefer boring code, patterns flourish gradually and naturally. Once a pattern emerges, then we can decide whether an abstraction is warranted and refactor our code fittingly. A time-honed well-placed abstraction is likely to cover more use cases than it might have covered if we had gone for an abstraction as soon as we had two or three functionally comparable pieces of code.
925
925
926
-
==== 9.7 Asynchronous Code Flows
926
+
=== 9.7 Asynchronous Code Flows
927
927
928
-
callbacks, promises, events, async/await, generators, etc.
928
+
In chapter 4 we discussed how many of the different ways in which we can manage complexity in asynchronous operations work, and how we can use them. Callbacks, events, Promises, generators, async functions, and async iterators, external libraries, and the list goes on. You should now be comfortable with how these constructs work, but when should you use them?
929
+
930
+
Callbacks are the most primitive solution. They require little knowledge beyond basic JavaScript, making callback-based code some of the easiest to read. Callbacks should be approached with care in cases where the flow of operations involves a long dependency chain, as a series of deeply nested asynchronous operations can lead to callback hell.
931
+
932
+
When it comes to callbacks, libraries like +async+footnote:[You can find the +async+ flow control library on GitHub: https://ponyfoo.com/s/pes-async-library.] can help reduce complexity when we have three or more related tasks that need to be executed asynchronously. Another positive aspect of these libraries is how they unobtrusively interoperate with plain callbacks, which is useful when we have a mix of complex flows that need to be abstracted through the library and simpler flows that you can articulate with plain callbacks.
933
+
934
+
Events are a cheap way of introducing extensibility into code flows, asynchronous or otherwise. Event don't lend themselves well to managing the complexity of asynchronous tasks, however.
935
+
936
+
The following example shows how convoluted our code could become if we wanted to handle asynchronous tasks using events. Half of the lines of code are spent on defining the code flow, and even then the flow is quite hard to understand. This means we probably chose the wrong tool for the job.
937
+
938
+
[source,javascript]
939
+
----
940
+
const tracker = emitter()
941
+
tracker.on(`started`, multiply)
942
+
tracker.on(`multiplied`, print)
943
+
start(256, 512, 1024)
944
+
function start (...input) {
945
+
const sum = input.reduce((a, b) => a + b, 0)
946
+
tracker.emit(`started`, { sum, input })
947
+
}
948
+
function multiply ({ sum, input }) {
949
+
const message = `The sum of ${ input.join(`+`) } is ${ sum }`
950
+
tracker.emit(`multiplied`, message)
951
+
}
952
+
function print (message) {
953
+
console.log(message)
954
+
}
955
+
----
956
+
957
+
Promises were around for a long time, in user libraries, before TC39 decided to bring them into the core JavaScript language. They serve a similar purpose as callback libraries, offering an alternative way of writing asynchronous code flows.
958
+
959
+
Promises are a bit more expensive than callbacks in terms of commitment, because promise chains involve more promises, so they are hard to interleave with plain callbacks. At the same time, you don't want to interleave promises with callback-based code, because that leads to complex applications. For any given portion of code, it's important to pick one paradigm and stick with it.
960
+
961
+
Committing to promises isn't inherently bad, though, but rather a cost you need to be aware of. As more and more of the web platform relies on promises as a fundamental building block, they only get better. Promises underlie generators, async functions, async iterators and async generators. The more we use those constructs, the more synergistic our applications become, and while it could be argued that plain callbacks are already synergistic by nature, they certainly don't compare to the sheer power of async functions and all promise-based solutions that are now native to the JavaScript language.
962
+
963
+
Generators...
964
+
965
+
Async Functions...
966
+
967
+
Async Iterators and Async Generators...
968
+
969
+
Consistency is an important theme when it comes to maintainable code..
0 commit comments