Skip to content

Commit 02d5536

Browse files
committed
Go over Chapter 21
1 parent 96fc4c6 commit 02d5536

File tree

3 files changed

+72
-69
lines changed

3 files changed

+72
-69
lines changed

11_async.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -921,7 +921,7 @@ async function findInStorage(nest, name) {
921921
let found = await routeRequest(nest, source, "storage",
922922
name);
923923
if (found != null) return found;
924-
} catch(_) {}
924+
} catch (_) {}
925925
}
926926
throw new Error("Not found");
927927
}
@@ -1069,7 +1069,7 @@ try {
10691069
setTimeout(() => {
10701070
throw new Error("Woosh");
10711071
}, 20);
1072-
} catch(_) {
1072+
} catch (_) {
10731073
// This will not run
10741074
console.log("Caught!");
10751075
}

21_skillsharing.md

Lines changed: 69 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -65,9 +65,9 @@ The ((application)) will be set up to show a _live_ view of the
6565
current proposed talks and their comments. Whenever someone,
6666
somewhere, submits a new talk or adds a comment, all people who have
6767
the page open in their browsers should immediately see the change.
68-
This poses a bit of a challenge. There is no way for a web server to
68+
This poses a bit of a challenge—there is no way for a web server to
6969
open a connection to a client, nor is there a good way to know which
70-
clients currently are looking at a given website.
70+
clients are currently looking at a given website.
7171

7272
{{index "Node.js"}}
7373

@@ -76,29 +76,31 @@ happens to be one of the motivations for Node's design.
7676

7777
## Long polling
7878

79-
{{index firewall, router, notification, "long polling"}}
79+
{{index firewall, notification, "long polling", network}}
8080

8181
To be able to immediately notify a client that something changed, we
8282
need a ((connection)) to that client. Since web ((browser))s do not
83-
traditionally accept connections and clients are usually behind
84-
devices that would block such connections anyway, having the server
85-
initiate this connection is not practical.
83+
traditionally accept connections and clients are often behind
84+
((router))s that would block such connections anyway, having the
85+
server initiate this connection is not practical.
8686

8787
We can arrange for the client to open the connection and keep it
8888
around so that the server can use it to send information when it needs
8989
to do so.
9090

91+
{{index socket}}
92+
9193
But an ((HTTP)) request allows only a simple flow of information: the
9294
client sends a request, the server comes back with a single response,
9395
and that is it. There is a technology called _((web sockets))_,
9496
supported by modern browsers, which makes it possible to open
9597
((connection))s for arbitrary data exchange. But using them properly
9698
is somewhat tricky.
9799

98-
In this chapter, we will use a simpler technique—((long
99-
polling))—where clients continuously ask the server for new
100-
information using regular HTTP requests, and the server stalls its
101-
answer when it has nothing new to report.
100+
In this chapter, we use a simpler technique—((long polling))—where
101+
clients continuously ask the server for new information using regular
102+
HTTP requests, and the server stalls its answer when it has nothing
103+
new to report.
102104

103105
{{index "live view"}}
104106

@@ -108,19 +110,19 @@ becomes available. For example, if Fatma has our skill-sharing
108110
application open in her browser, that browser will have made a request
109111
for updates and be waiting for a response to that request. When Iman
110112
submits a talk on Extreme Downhill Unicycling, the server will notice
111-
that Fatma is waiting for updates and send information about the new
112-
talk as a response to her pending request. Fatma's browser will
113-
receive the data and update the screen to show the talk.
113+
that Fatma is waiting for updates and send a response containing the
114+
new talk to her pending request. Fatma's browser will receive the data
115+
and update the screen to show the talk.
114116

115117
{{index robustness, timeout}}
116118

117119
To prevent connections from timing out (being aborted because of a
118-
lack of activity), ((long-polling)) techniques usually set a maximum
120+
lack of activity), ((long polling)) techniques usually set a maximum
119121
time for each request, after which the server will respond anyway,
120-
even though it has nothing to report, and the client will start a new
121-
request. Periodically restarting the request also makes the technique
122-
more robust, allowing clients to recover from temporary ((connection))
123-
failures or server problems.
122+
even though it has nothing to report, after which the client will
123+
start a new request. Periodically restarting the request also makes
124+
the technique more robust, allowing clients to recover from temporary
125+
((connection)) failures or server problems.
124126

125127
{{index "Node.js"}}
126128

@@ -139,12 +141,12 @@ which they communicate.
139141

140142
{{index [path, URL]}}
141143

142-
We will base our interface on ((JSON)), and like in the file server
143-
from [Chapter ?](node#file_server), we'll try to make good use of HTTP
144-
((method))s and ((header))s. The interface is centered around the
145-
`/talks` path. Paths that do not start with `/talks` will be used for
146-
serving ((static file))s—the HTML and JavaScript code for the
147-
client-side system.
144+
We will use ((JSON)) as the format of our request and response body.
145+
Like in the file server from [Chapter ?](node#file_server), we'll try
146+
to make good use of HTTP ((method))s and ((header))s. The interface is
147+
centered around the `/talks` path. Paths that do not start with
148+
`/talks` will be used for serving ((static file))s—the HTML and
149+
JavaScript code for the client-side system.
148150

149151
{{index "GET method"}}
150152

@@ -220,8 +222,8 @@ Clients, when they later request that resource again, may make a
220222
_((conditional request))_ by including an `If-None-Match` header whose
221223
value holds that same string. If the resource hasn't changed, the
222224
server will respond with status code 304, which means "not modified",
223-
telling the client that its cached version is still current. If the
224-
tag does not match, the server will respond as normal.
225+
telling the client that its cached version is still current. When the
226+
tag does not match the server responds as normal.
225227

226228
{{index "Prefer header"}}
227229

@@ -235,8 +237,8 @@ conditional requests, we give them another header `Prefer: wait=90`,
235237
which tells the server that the client is willing wait up to 90
236238
seconds for the response.
237239

238-
So the server keeps a version number that it updates every time the
239-
talks change, and uses that as the `ETag` value. And clients can make
240+
The server will keep a version number that it updates every time the
241+
talks change, and uses that as the `ETag` value. Clients can make
240242
requests like this to be notified when the talks change:
241243

242244
```{lang: null}
@@ -265,7 +267,7 @@ without further protection probably wouldn't end well.)
265267

266268
{{index "skill-sharing project"}}
267269

268-
Let's start by writing the ((server))-side part of the program. The
270+
Let's start by building the ((server))-side part of the program. The
269271
code in this section runs on ((Node.js)).
270272

271273
### Routing
@@ -286,11 +288,11 @@ that `PUT` requests with a path that matches the regular expression
286288
`/^\/talks\/([^\/]+)$/` (`/talks/` followed by a talk title) can be
287289
handled by a given function. In addition, it can help extract the
288290
meaningful parts of the path, in this case the talk title, wrapped in
289-
parentheses in the ((regular expression)) and pass those to the
291+
parentheses in the ((regular expression)), and pass those to the
290292
handler function.
291293

292-
There are a number of good router packages on ((NPM)), but here we
293-
will write one ourselves to illustrate the principle.
294+
There are a number of good router packages on ((NPM)), but here we'll
295+
write one ourselves to illustrate the principle.
294296

295297
{{index "require function", "Router class", module}}
296298

@@ -407,7 +409,8 @@ class SkillShareServer {
407409

408410
This uses a similar convention as the file server from the [previous
409411
chapter](node) for responses—handlers return promises that resolve to
410-
objects that describe the response.
412+
objects describing the response. It wraps the server in an object that
413+
also holds its state.
411414

412415
### Talks as resources
413416

@@ -452,15 +455,15 @@ router.add("DELETE", talkPath, async (server, title) => {
452455

453456
{{index "long polling", "updated method"}}
454457

455-
The `updated` function, which we will define
456-
[later](skillsharing#updated), notifies waiting long-polling requests
458+
The `updated` method, which we will define
459+
[later](skillsharing#updated), notifies waiting long polling requests
457460
about the change.
458461

459-
{{index "readStreamAsJSON function", "body (HTTP)"}}
462+
{{index "readStream function", "body (HTTP)", stream}}
460463

461464
To retrieve the content of a request body, we define a function called
462-
`readStream`, which reads all content from a stream and returns a
463-
promise that resolves to a string.
465+
`readStream`, which reads all content from a ((readable stream)) and
466+
returns a promise that resolves to a string.
464467

465468
```{includeCode: ">code/skillsharing/skillsharing_server.js"}
466469
function readStream(stream) {
@@ -479,8 +482,8 @@ One handler that needs to read request bodies is the `PUT` handler,
479482
which is used to create new ((talk))s. It has to check whether the
480483
data it was given has `presenter` and `summary` properties which are
481484
strings. Any data coming from outside the system might be nonsense,
482-
and we don't want to corrupt our internal data model, or even
483-
((crash)), when bad requests come in.
485+
and we don't want to corrupt our internal data model or ((crash)) when
486+
bad requests come in.
484487

485488
{{index "updated method"}}
486489

@@ -542,11 +545,11 @@ router.add("POST", /^\/talks\/([^\/]+)\/comments$/,
542545

543546
Trying to add a comment to a nonexistent talk returns a 404 error.
544547

545-
### Long-polling support
548+
### Long polling support
546549

547550
The most interesting aspect of the server is the part that handles
548-
((long polling)). When a `GET` request comes in for `/talks`, it can
549-
be either a simple request for all talks or a long-polling request.
551+
((long polling)). When a `GET` request comes in for `/talks`, it may
552+
either be a regular request or a long polling request.
550553

551554
{{index "talkResponse method", "ETag header"}}
552555

@@ -597,12 +600,12 @@ If the request is conditional and the talks did not change, we consult
597600
the `Prefer` header to see if we should delay the response or respond
598601
right away.
599602

600-
{{index "304 (HTTP status code)", "setTimeout function", timeout}}
603+
{{index "304 (HTTP status code)", "setTimeout function", timeout, "callback function"}}
601604

602-
A ((callback function)) for delayed requests is stored in the server's
605+
Callback functions for delayed requests are stored in the server's
603606
`waiting` array, so that they can be notified when something happens.
604607
The `waitForChanges` method also immediately sets a timer to respond
605-
normally, with a 304 status, when the request has waited long enough.
608+
with a 304 status when the request has waited long enough.
606609

607610
```{includeCode: ">code/skillsharing/skillsharing_server.js"}
608611
SkillShareServer.prototype.waitForChanges = function(time) {
@@ -621,8 +624,8 @@ SkillShareServer.prototype.waitForChanges = function(time) {
621624

622625
{{id updated}}
623626

624-
Registering a change with `updated` will increase the `version` field
625-
and wake up all waiting requests.
627+
Registering a change with `updated` increases the `version` property
628+
and wakes up all waiting requests.
626629

627630
```{includeCode: ">code/skillsharing/skillsharing_server.js"}
628631
SkillShareServer.prototype.updated = function() {
@@ -646,7 +649,7 @@ new SkillShareServer(Object.create(null)).start(8000);
646649

647650
{{index "skill-sharing project"}}
648651

649-
The ((client))-side part of the talk-managing website consists of
652+
The ((client))-side part of the skill-sharing website consists of
650653
three files: a tiny HTML page, a style sheet, and a JavaScript file.
651654

652655
### HTML
@@ -679,8 +682,8 @@ It defines the document ((title)) and includes a ((style sheet)),
679682
which defines a few styles to, among other things, make sure there is
680683
some space between talks.
681684

682-
Finally, it adds a heading at the top of the page and loads the script
683-
that contains the ((client))-side application.
685+
At the bottom, it adds a heading at the top of the page and loads the
686+
script that contains the ((client))-side application.
684687

685688
### Actions
686689

@@ -694,7 +697,7 @@ user is trying to do.
694697

695698
The `handleAction` function takes such an action and makes it happen.
696699
Because our state updates are so simple, state changes are handled in
697-
the same function—there's no separate ((reducer)) function.
700+
the same function.
698701

699702
```{includeCode: ">code/skillsharing/public/skillsharing_client.js", test: no}
700703
function handleAction(state, action) {
@@ -752,8 +755,8 @@ function fetchOK(url, options) {
752755

753756
{{index "talkURL function", "encodeURIComponent function"}}
754757

755-
This helper function is used to build up a ((URL)) for a talks with a
756-
given title.
758+
And this helper function is used to build up a ((URL)) for a talks
759+
with a given title.
757760

758761
```{includeCode: ">code/skillsharing/public/skillsharing_client.js", test: no}
759762
function talkURL(title) {
@@ -780,8 +783,8 @@ function reportError(error) {
780783

781784
We'll use an approach similar to the one we saw in [Chapter ?](paint),
782785
splitting the application into components. But since some of the
783-
components either never need to update or are always full redrawn when
784-
updated, we'll define those not as classes, but as functions that
786+
components either never need to update or are always fully redrawn
787+
when updated, we'll define those not as classes, but as functions that
785788
directly return a DOM node. For example, here is a component that
786789
shows the field where the user can enter their name:
787790

@@ -799,8 +802,8 @@ function renderUserField(name, dispatch) {
799802

800803
{{index "elt function"}}
801804

802-
To construct DOM elements, we'll use the `elt` function from [Chapter
803-
?](paint) again.
805+
The `elt` function used to construct DOM elements is the one we used
806+
in [Chapter ?](paint).
804807

805808
```{includeCode: ">code/skillsharing/public/skillsharing_client.js", test: no, hidden: true}
806809
function elt(type, props, ...children) {
@@ -899,7 +902,7 @@ function renderTalkForm(dispatch) {
899902

900903
{{index "pollTalks function", "long polling", "If-None-Match header", "Prefer header", "fetch function"}}
901904

902-
To start the app, we need the current list of talks. Since the initial
905+
To start the app we need the current list of talks. Since the initial
903906
load is closely related to the long polling process—the `ETag` from
904907
the load must be used when polling—we'll write a function that keeps
905908
polling the server for `/talks`, and calls a ((callback function))
@@ -921,16 +924,16 @@ async function pollTalks(update) {
921924
continue;
922925
}
923926
if (response.status == 304) continue;
924-
update(await response.json());
925927
tag = response.headers.get("ETag");
928+
update(await response.json());
926929
}
927930
}
928931
```
929932

930933
{{index "async function"}}
931934

932-
This is an `async` function, to make looping and waiting for the
933-
request easier. It runs an infinite loop that, on each iteration,
935+
This is an `async` function, so that looping and waiting for the
936+
request is easier. It runs an infinite loop that, on each iteration,
934937
retrieves the list of talks—either normally or, if this isn't the
935938
first request, with the headers included that make it a long polling
936939
request.
@@ -985,7 +988,7 @@ class SkillShareApp {
985988
{{index synchronization, "live view"}}
986989

987990
When the talks change, this component redraws all of them. This is
988-
simple, but also very crude. We'll get back to that in the exercises.
991+
simple, but also wasteful. We'll get back to that in the exercises.
989992

990993
We can start the application like this:
991994

@@ -1024,9 +1027,9 @@ in the other.
10241027
The following exercises will involve modifying the system defined in
10251028
this chapter. To work on them, make sure you ((download)) the code
10261029
first
1027-
([_eloquentjavascript.net/code/skillsharing.zip_](https://eloquentjavascript.net/code/skillsharing.zip))
1028-
and have Node installed [_nodejs.org_](https://nodejs.org), and install
1029-
the project's dependency with `npm install`.
1030+
([_eloquentjavascript.net/code/skillsharing.zip_](https://eloquentjavascript.net/code/skillsharing.zip)),
1031+
have Node installed [_nodejs.org_](https://nodejs.org), and have
1032+
installed the project's dependency with `npm install`.
10301033

10311034
### Disk persistence
10321035

src/render_html.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ function highlight(lang, text) {
3737

3838
function maybeSplitInlineCode(html) {
3939
if (html.length <= 16) return html
40-
return html.replace(/[.\/](?!\/)/, "$&<wbr>")
40+
return html.replace(/[.\/](?!\/)/g, "$&<wbr>")
4141
}
4242

4343
function anchor(token) {

0 commit comments

Comments
 (0)