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
One approach to ((asynchronous programming)) is to make functions that need to wait for something take an extra argument, a _((callback function))_. Such a function starts some process, sets things up so that the callback function is called when the process finishes, and then returns.
62
+
One approach to ((asynchronous programming)) is to make functions that need to wait for something take an extra argument, a _((callback function))_. The asynchronous a function starts some process, sets things up so that the callback function is called when the process finishes, and then returns.
63
63
64
64
{{index "setTimeout function", waiting}}
65
65
@@ -69,7 +69,7 @@ As an example, the `setTimeout` function, available both in Node.js and in brows
69
69
setTimeout(() => console.log("Tick"), 500);
70
70
```
71
71
72
-
Waiting is not generally a very important type of work, but it can be very useful when updating an animation or checking whether some other action is taking longer than expected.
72
+
Waiting is not generally a very important type of work, but it can be very useful when you need to arrange for something to happen at a certain time or check whether some other action is taking longer than expected.
73
73
74
74
{{index "readTextFile function"}}
75
75
@@ -159,7 +159,7 @@ The function returns the result of this chain of `then` calls. The initial promi
159
159
160
160
In this code, the functions used in the first two `then` calls return a regular value, which will immediately be passed into the promise returned by `then` when the function returns. The last one returns a promise (`textFile(filename)`), making it an actual asynchronous step.
161
161
162
-
In this case, it would have also been possible to do all these steps inside a single `then` callback, since only the last step is actually asynchronous. But the kind of `then` wrappers that only do some synchronous data transformation are often useful, for example when you want to return a promise that produces a processed version of some asynchronous result.
162
+
Tt would have also been possible to do all these steps inside a single `then` callback, since only the last step is actually asynchronous. But the kind of `then` wrappers that only do some synchronous data transformation are often useful, for example when you want to return a promise that produces a processed version of some asynchronous result.
163
163
164
164
```
165
165
function jsonFile(filename) {
@@ -212,7 +212,7 @@ A function passed to the `Promise` constructor receives a second argument, along
212
212
213
213
{{index "textFile function"}}
214
214
215
-
The `readTextFile` function also uses the convention of passing the error as a second argument, if there is a problem. Our `textFile` wrapper should actually look at that argument, so that a failure causes the promise it returns to be rejected.
215
+
When our `readTextFile` function encounters a problem, it passes the error to its callback function as a second argument. Our `textFile` wrapper should actually look at that argument, so that a failure causes the promise it returns to be rejected.
216
216
217
217
```{includeCode: true}
218
218
function textFile(filename) {
@@ -239,6 +239,8 @@ new Promise((_, reject) => reject(new Error("Fail")))
239
239
// → Handler 2: nothing
240
240
```
241
241
242
+
The first regular handler function isn't called, because at that point of the pipeline the promise holds a rejection. The `catch` handler handles that rejection and returns a value, which is given to the second handler function.
Much like an uncaught exception is handled by the environment, JavaScript environments can detect when a promise rejection isn't handled and will report this as an error.
@@ -253,11 +255,11 @@ One of the crows stands out—a large scruffy female with a few white feathers i
253
255
254
256
Contrary to the rest of the group, who look like they are happy to spend the day goofing around here, the large crow looks purposeful. Carrying her loot, she flies straight towards the roof of the hangar building, disappearing into an air vent.
255
257
256
-
Inside the building, in a narrow space under the roof of an unfinished stairwell, you can hear an odd tapping sound—soft, but persistent. The crow is sitting there, surrounded by her stolen snack, half a dozen smart phones (several of which are turned on), and a mess of cables. She rapidly taps the screen of one of the phones with her beak. Words are appearing on it. If you didn't know better, you'd think she was typing.
258
+
Inside the building, you can hear an odd tapping sound—soft, but persistent. It comes from a narrow space under the roof of an unfinished stairwell. The crow is sitting there, surrounded by her stolen snack, half a dozen smart phones (several of which are turned on), and a mess of cables. She rapidly taps the screen of one of the phones with her beak. Words are appearing on it. If you didn't know better, you'd think she was typing.
257
259
258
-
This crow is known to her peers as “cāāw-krö“, but since that is hard for us to remember, we'll call her Carla.
260
+
This crow is known to her peers as “cāāw-krö”. But since those sounds are poorly suited for human vocal chords, we'll refer to her as Carla.
259
261
260
-
Carla is a somewhat peculiar crow. In her youth, she was fascinated by human language, eavesdropping on people until she could understand roughly what they were saying. Later in life, her interest shifted to human technology, and she started stealing phones to study them. Her current project is learning to program. The text she is typing in her hidden lab is, in fact, a piece of JavaScript code.
262
+
Carla is a somewhat peculiar crow. In her youth, she was fascinated by human language, eavesdropping on people until she had a good grasp of what they were saying. Later in life, her interest shifted to human technology, and she started stealing phones to study them. Her current project is learning to program. The text she is typing in her hidden lab is, in fact, a piece of JavaScript code.
261
263
262
264
## Breaking In
263
265
@@ -280,7 +282,7 @@ function withTimeout(promise, time) {
280
282
}
281
283
```
282
284
283
-
This makes use of the fact that a promise can only be resolved or rejected once—if the given promise resolves or rejects first, that result will be the result of the promise returned by `withTimeout`. If, on the other hand, the `setTimeout` fires first, rejecting the promise, any further resolve or reject calls are ignored.
285
+
This makes use of the fact that a promise can only be resolved or rejected once—if the promise given as argument resolves or rejects first, that result will be the result of the promise returned by `withTimeout`. If, on the other hand, the `setTimeout` fires first, rejecting the promise, any further resolve or reject calls are ignored.
284
286
285
287
To find the whole passcode, we need to repeatedly look for the next digit by trying each digit. If authentication succeeds, we know we have found what we are looking for. If it immediately fails, we know that digit was wrong, and must try the next digit. If the request times out, we have found another correct digit, and must continue by adding another digit.
286
288
@@ -323,7 +325,7 @@ Even with promises, this kind of asynchronous code is annoying to write. Promise
The thing the cracking function actually does is completely linear—it always waits for the previous action to complete before starting the next one. In a synchronous programming model, this'd be simpler to express.
328
+
The thing the cracking function actually does is completely linear—it always waits for the previous action to complete before starting the next one. In a synchronous programming model, it'd be more straightforward to express.
327
329
328
330
{{index "async function", "await keyword"}}
329
331
@@ -494,6 +496,8 @@ function displayFrame(frame) {
494
496
This maps over the images in `frame` (which is an array of display data arrays) to create an array
495
497
of request promises. It then returns a promise that combines all of those.
496
498
499
+
In order to be able to stop a playing video, the process is wrapped in a class. This class has an asynchronous `play` method that returns a promise that only resolves when the playback is stopped again via the `stop` method.
500
+
497
501
```{includeCode: true}
498
502
function wait(time) {
499
503
return new Promise(accept => setTimeout(accept, time));
@@ -523,8 +527,6 @@ class VideoPlayer {
523
527
524
528
The `wait` function wraps `setTimeout` in a promise that resolves after the given amount of milliseconds. This is useful for controlling the speed of the playback.
525
529
526
-
In order to be able to stop a playing video, the process is wrapped in a class. This class has an asynchronous `play` method that returns a promise that only resolves when the playback is stopped again via the `stop` method.
527
-
528
530
```
529
531
let video = new VideoPlayer(clipImages, 100);
530
532
video.play().catch(e => {
@@ -533,6 +535,8 @@ video.play().catch(e => {
533
535
setTimeout(() => video.stop(), 15000);
534
536
```
535
537
538
+
For the entire week that the screen wall stands, every evening, when it is dark, a huge glowing orange bird mysteriously appears on it.
@@ -611,11 +615,11 @@ async function fileSizes(files) {
611
615
612
616
{{index "async function"}}
613
617
614
-
The `async name =>` part shows how ((arrow function))s can also be made `async` by putting the word `async` in front of them.
618
+
The `async fileName =>` part shows how ((arrow function))s can also be made `async` by putting the word `async` in front of them.
615
619
616
620
{{index "Promise.all function"}}
617
621
618
-
The code doesn't immediately look suspicious...it maps the `async` arrow function over the array of names, creating an array of promises, and then uses `Promise.all` to wait for all of these before returning the list they build up.
622
+
The code doesn't immediately look suspicious...it maps the `async` arrow function over the array of names, creating an array of promises, and then uses `Promise.all` to wait for all of these before returning the list they build up.
619
623
620
624
But it is entirely broken. It'll always return only a single line of output, listing the file that took the longest to read.
621
625
@@ -672,7 +676,7 @@ There's a security camera near Carla's lab that's activated by a motion sensor.
She's also been logging the times at which the camera is tripped for a while, and wants to use this information to visualize which times, in an average week, tend to be quiet, and which tend to be busy. The log is stored in files holding one time stamp number (as returned by `Date.now()` per line.
679
+
She's also been logging the times at which the camera is tripped for a while, and wants to use this information to visualize which times, in an average week, tend to be quiet, and which tend to be busy. The log is stored in files holding one time stamp number (as returned by `Date.now()`) per line.
Copy file name to clipboardExpand all lines: 21_skillsharing.md
+44-33Lines changed: 44 additions & 33 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -193,16 +193,18 @@ export class Router {
193
193
add(method, url, handler) {
194
194
this.routes.push({method, url, handler});
195
195
}
196
-
resolve(context, request) {
197
-
let path = parse(request.url).pathname;
198
-
199
-
for (let {method, url, handler} of this.routes) {
200
-
let match = url.exec(path);
201
-
if (!match || request.method != method) continue;
202
-
let urlParts = match.slice(1).map(decodeURIComponent);
203
-
return handler(context, ...urlParts, request);
204
-
}
205
-
return null;
196
+
}
197
+
198
+
async function resolveRequest(router, context, request) {
199
+
let path = parse(request.url).pathname;
200
+
201
+
for (let {method, url, handler} of this.routes) {
202
+
let match = url.exec(path);
203
+
if (!match || request.method != method) continue;
204
+
let urlParts = match.slice(1).map(decodeURIComponent);
205
+
let response =
206
+
await handler(context, ...urlParts, request);
207
+
if (response) return response;
206
208
}
207
209
}
208
210
```
@@ -223,40 +225,30 @@ The handler functions are called with the `context` value (which will be the ser
223
225
224
226
When a request matches none of the request types defined in our router, the server must interpret it as a request for a file in the `public` directory. It would be possible to use the file server defined in [Chapter ?](node#file_server) to serve such files, but we neither need nor want to support `PUT` and `DELETE` requests on files, and we would like to have advanced features such as support for caching. So let's use a solid, well-tested ((static file)) server from ((NPM)) instead.
I opted for `ecstatic`. This isn't the only such server on NPM, but it works well and fits our purposes. The `ecstatic` package exports a function that can be called with a configuration object to produce a request handler function. We use the `root` option to tell the server where it should look for files. The handler function accepts `request` and `response`parameters and can be passed directly to `createServer` to create a server that serves _only_ files. We want to first check for requests that we should handle specially, though, so we wrap it in another function.
230
+
I opted for `serve-static`. This isn't the only such server on NPM, but it works well and fits our purposes. The `serve-static` package exports a function that can be called with a root directory to produce a request handler function. The handler function accepts the `request` and `response`arguments provided by the server, and a third argument, a function that it will call if no file matches the request. We want our server to first check for requests that we should handle specially, as defined in the router, so we wrap it in another function.
This uses a similar convention as the file server from the [previous chapter](node) for responses—handlers return promises that resolve to objects describing the response. It wraps the server in an object that also holds its state.
263
+
The `serveFromRouter` function has the same interface as `fileServer`, taking `(request, response, next)` arguments. This allows us to “chain” several request handlers, allowing each to either handle the request, or pass responsibility for that on to the next handler. The final handler, `notFound`, simply responds with a “not found” error.
264
+
265
+
Our `serveFromRouter` function uses a similar convention as the file server from the [previous chapter](node) for responses—handler in the router return promises that resolve to objects describing the response.
let {body, status = 200, headers = defaultHeaders} =
275
+
await resolved.catch(error => {
276
+
if (error.status != null) return error;
277
+
return {body: String(error), status: 500};
278
+
});
279
+
response.writeHead(status, headers);
280
+
response.end(body);
281
+
}
282
+
```
272
283
273
284
### Talks as resources
274
285
@@ -479,7 +490,7 @@ The ((client))-side part of the skill-sharing website consists of three files: a
479
490
480
491
{{index "index.html"}}
481
492
482
-
It is a widely used convention for web servers to try to serve a file named `index.html` when a request is made directly to a path that corresponds to a directory. The ((file server)) module we use, `ecstatic`, supports this convention. When a request is made to the path `/`, the server looks for the file `./public/index.html` (`./public` being the root we gave it) and returns that file if found.
493
+
It is a widely used convention for web servers to try to serve a file named `index.html` when a request is made directly to a path that corresponds to a directory. The ((file server)) module we use, `serve-static`, supports this convention. When a request is made to the path `/`, the server looks for the file `./public/index.html` (`./public` being the root we gave it) and returns that file if found.
483
494
484
495
Thus, if we want a page to show up when a browser is pointed at our server, we should put it in `public/index.html`. This is our index file:
0 commit comments