@@ -65,9 +65,9 @@ The ((application)) will be set up to show a _live_ view of the
65
65
current proposed talks and their comments. Whenever someone,
66
66
somewhere, submits a new talk or adds a comment, all people who have
67
67
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
69
69
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.
71
71
72
72
{{index "Node.js"}}
73
73
@@ -76,29 +76,31 @@ happens to be one of the motivations for Node's design.
76
76
77
77
## Long polling
78
78
79
- {{index firewall, router, notification, "long polling"}}
79
+ {{index firewall, notification, "long polling", network }}
80
80
81
81
To be able to immediately notify a client that something changed, we
82
82
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.
86
86
87
87
We can arrange for the client to open the connection and keep it
88
88
around so that the server can use it to send information when it needs
89
89
to do so.
90
90
91
+ {{index socket}}
92
+
91
93
But an ((HTTP)) request allows only a simple flow of information: the
92
94
client sends a request, the server comes back with a single response,
93
95
and that is it. There is a technology called _ ((web sockets))_ ,
94
96
supported by modern browsers, which makes it possible to open
95
97
((connection))s for arbitrary data exchange. But using them properly
96
98
is somewhat tricky.
97
99
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.
102
104
103
105
{{index "live view"}}
104
106
@@ -108,19 +110,19 @@ becomes available. For example, if Fatma has our skill-sharing
108
110
application open in her browser, that browser will have made a request
109
111
for updates and be waiting for a response to that request. When Iman
110
112
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.
114
116
115
117
{{index robustness, timeout}}
116
118
117
119
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
119
121
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.
124
126
125
127
{{index "Node.js"}}
126
128
@@ -139,12 +141,12 @@ which they communicate.
139
141
140
142
{{index [ path, URL] }}
141
143
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.
148
150
149
151
{{index "GET method"}}
150
152
@@ -220,8 +222,8 @@ Clients, when they later request that resource again, may make a
220
222
_ ((conditional request))_ by including an ` If-None-Match ` header whose
221
223
value holds that same string. If the resource hasn't changed, the
222
224
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.
225
227
226
228
{{index "Prefer header"}}
227
229
@@ -235,8 +237,8 @@ conditional requests, we give them another header `Prefer: wait=90`,
235
237
which tells the server that the client is willing wait up to 90
236
238
seconds for the response.
237
239
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
240
242
requests like this to be notified when the talks change:
241
243
242
244
``` {lang: null}
@@ -265,7 +267,7 @@ without further protection probably wouldn't end well.)
265
267
266
268
{{index "skill-sharing project"}}
267
269
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
269
271
code in this section runs on ((Node.js)).
270
272
271
273
### Routing
@@ -286,11 +288,11 @@ that `PUT` requests with a path that matches the regular expression
286
288
` /^\/talks\/([^\/]+)$/ ` (` /talks/ ` followed by a talk title) can be
287
289
handled by a given function. In addition, it can help extract the
288
290
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
290
292
handler function.
291
293
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.
294
296
295
297
{{index "require function", "Router class", module}}
296
298
@@ -407,7 +409,8 @@ class SkillShareServer {
407
409
408
410
This uses a similar convention as the file server from the [ previous
409
411
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.
411
414
412
415
### Talks as resources
413
416
@@ -452,15 +455,15 @@ router.add("DELETE", talkPath, async (server, title) => {
452
455
453
456
{{index "long polling", "updated method"}}
454
457
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
457
460
about the change.
458
461
459
- {{index "readStreamAsJSON function", "body (HTTP)"}}
462
+ {{index "readStream function", "body (HTTP)", stream }}
460
463
461
464
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.
464
467
465
468
``` {includeCode: ">code/skillsharing/skillsharing_server.js"}
466
469
function readStream(stream) {
@@ -479,8 +482,8 @@ One handler that needs to read request bodies is the `PUT` handler,
479
482
which is used to create new ((talk))s. It has to check whether the
480
483
data it was given has ` presenter ` and ` summary ` properties which are
481
484
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.
484
487
485
488
{{index "updated method"}}
486
489
@@ -542,11 +545,11 @@ router.add("POST", /^\/talks\/([^\/]+)\/comments$/,
542
545
543
546
Trying to add a comment to a nonexistent talk returns a 404 error.
544
547
545
- ### Long- polling support
548
+ ### Long polling support
546
549
547
550
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.
550
553
551
554
{{index "talkResponse method", "ETag header"}}
552
555
@@ -597,12 +600,12 @@ If the request is conditional and the talks did not change, we consult
597
600
the ` Prefer ` header to see if we should delay the response or respond
598
601
right away.
599
602
600
- {{index "304 (HTTP status code)", "setTimeout function", timeout}}
603
+ {{index "304 (HTTP status code)", "setTimeout function", timeout, "callback function" }}
601
604
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
603
606
` waiting ` array, so that they can be notified when something happens.
604
607
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.
606
609
607
610
``` {includeCode: ">code/skillsharing/skillsharing_server.js"}
608
611
SkillShareServer.prototype.waitForChanges = function(time) {
@@ -621,8 +624,8 @@ SkillShareServer.prototype.waitForChanges = function(time) {
621
624
622
625
{{id updated}}
623
626
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.
626
629
627
630
``` {includeCode: ">code/skillsharing/skillsharing_server.js"}
628
631
SkillShareServer.prototype.updated = function() {
@@ -646,7 +649,7 @@ new SkillShareServer(Object.create(null)).start(8000);
646
649
647
650
{{index "skill-sharing project"}}
648
651
649
- The ((client))-side part of the talk-managing website consists of
652
+ The ((client))-side part of the skill-sharing website consists of
650
653
three files: a tiny HTML page, a style sheet, and a JavaScript file.
651
654
652
655
### HTML
@@ -679,8 +682,8 @@ It defines the document ((title)) and includes a ((style sheet)),
679
682
which defines a few styles to, among other things, make sure there is
680
683
some space between talks.
681
684
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.
684
687
685
688
### Actions
686
689
@@ -694,7 +697,7 @@ user is trying to do.
694
697
695
698
The ` handleAction ` function takes such an action and makes it happen.
696
699
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.
698
701
699
702
``` {includeCode: ">code/skillsharing/public/skillsharing_client.js", test: no}
700
703
function handleAction(state, action) {
@@ -752,8 +755,8 @@ function fetchOK(url, options) {
752
755
753
756
{{index "talkURL function", "encodeURIComponent function"}}
754
757
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.
757
760
758
761
``` {includeCode: ">code/skillsharing/public/skillsharing_client.js", test: no}
759
762
function talkURL(title) {
@@ -780,8 +783,8 @@ function reportError(error) {
780
783
781
784
We'll use an approach similar to the one we saw in [ Chapter ?] ( paint ) ,
782
785
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
785
788
directly return a DOM node. For example, here is a component that
786
789
shows the field where the user can enter their name:
787
790
@@ -799,8 +802,8 @@ function renderUserField(name, dispatch) {
799
802
800
803
{{index "elt function"}}
801
804
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 ) .
804
807
805
808
``` {includeCode: ">code/skillsharing/public/skillsharing_client.js", test: no, hidden: true}
806
809
function elt(type, props, ...children) {
@@ -899,7 +902,7 @@ function renderTalkForm(dispatch) {
899
902
900
903
{{index "pollTalks function", "long polling", "If-None-Match header", "Prefer header", "fetch function"}}
901
904
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
903
906
load is closely related to the long polling process—the ` ETag ` from
904
907
the load must be used when polling—we'll write a function that keeps
905
908
polling the server for ` /talks ` , and calls a ((callback function))
@@ -921,16 +924,16 @@ async function pollTalks(update) {
921
924
continue;
922
925
}
923
926
if (response.status == 304) continue;
924
- update(await response.json());
925
927
tag = response.headers.get("ETag");
928
+ update(await response.json());
926
929
}
927
930
}
928
931
```
929
932
930
933
{{index "async function"}}
931
934
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,
934
937
retrieves the list of talks—either normally or, if this isn't the
935
938
first request, with the headers included that make it a long polling
936
939
request.
@@ -985,7 +988,7 @@ class SkillShareApp {
985
988
{{index synchronization, "live view"}}
986
989
987
990
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.
989
992
990
993
We can start the application like this:
991
994
@@ -1024,9 +1027,9 @@ in the other.
1024
1027
The following exercises will involve modifying the system defined in
1025
1028
this chapter. To work on them, make sure you ((download)) the code
1026
1029
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 ` .
1030
1033
1031
1034
### Disk persistence
1032
1035
0 commit comments