Skip to content

Commit 7f6db5b

Browse files
author
davidmarkclements
committed
ch 3 second draft
1 parent 6e78447 commit 7f6db5b

File tree

2 files changed

+60
-49
lines changed

2 files changed

+60
-49
lines changed

Diff for: 03-Coordinating-IO.pdf

78.4 KB
Binary file not shown.

Diff for: 03-Coordinating-IO/content.md

+60-49
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,16 @@
1-
# 2 Coordinating I/O
1+
# 3 Coordinating I/O
22

33
This chapter covers the following topics
44

5-
* File reading, writing, appending
6-
* File system metadata
7-
* STDOUT, STDIN, STDERR
8-
* Communicating with sockets
5+
* Interfacing with standard I/O
6+
* Working with files
7+
* Fetching meta-data
8+
* Watching files and directories
9+
* Communicating over sockets
910

1011
## Introduction
1112

12-
Operationally, Node.js is C with JavaScript's clothes on.
13+
Operationally, Node.js is C/C++ with JavaScript's clothes on.
1314
Just like C and other low-level environments, Node interacts with the Operating System at a fundamental level: Input and Output.
1415

1516
In this chapter we'll explore some core API's provided by Node, along with
@@ -18,12 +19,11 @@ the file system, and the network stack.
1819

1920
## Interfacing with standard I/O
2021

21-
Standard I/O relates to the predefined input, output and error data channels that connect a process to a shell terminal. Of course these can be redirected and piped
22-
to other programs for further processing, storage and so on.
22+
Standard I/O relates to the predefined input, output and error data channels that connect a process to a shell terminal, commonly known as STDIN, STDOUT and STDERR. Of course these can be redirected and piped to other programs for further processing, storage and so on.
2323

24-
Node provides access standard I/O on the global `process` object.
24+
Node provides access to standard I/O on the global `process` object.
2525

26-
In this recipe we're going to take some input, use it form output, and simultaneously log to the standard error channel.
26+
In this recipe we're going to take some input, use it to form some data which we'll send to STDOUT, while simultaneously logging to STDERR.
2727

2828
### Getting Ready
2929

@@ -97,7 +97,7 @@ aGkKdGhlcmUK
9797

9898
The standard I/O channels are implemented using Node.js Streams.
9999

100-
We'll find out more about these in **Chapter 3 Using Streams**, we also have an example in the **There's more** section of this recipe of using the standard
100+
We'll find out more about these in **Chapter 4 Using Streams**, we also have an example in the **There's more** section of this recipe of using the standard
101101
I/O channels as streams.
102102

103103
Suffice it to say, that Node `Stream` instances (instantiated from Node's core `stream` module) inherit from `EventEmitter` (from Node's core `events` module), and emit a `data` event for every chunk of data received.
@@ -119,7 +119,7 @@ Let's take a look at how Node Streams wrap Standard I/O channels, and how to det
119119

120120
#### Piping
121121

122-
As mentioned in the main recipe, the standard I/O channels available on the global `process` object are implementations of a core Node abstraction: streams. We'll be covering these in much greater detail in **Chapter 3 Using Streams** but for now let's see how we could achieve an equivalent effect using Node Streams' `pipe` method.
122+
As mentioned in the main recipe, the standard I/O channels available on the global `process` object are implementations of a core Node abstraction: streams. We'll be covering these in much greater detail in **Chapter 4 Using Streams** but for now let's see how we could achieve an equivalent effect using Node Streams' `pipe` method.
123123

124124
For this example we need the third party `base64-encode-stream` module, so let's open a terminal and run the following commands:
125125

@@ -170,8 +170,8 @@ true
170170
> result. There's also the related `-e` flag which only evaluates a
171171
> supplied command line string, but doesn't output it's return value.
172172
173-
We're running `node` directly, so the Standard In channel is correctly
174-
identified as a TTY.
173+
We're running `node` directly, so our STDIN is correctly identified as
174+
a TTY input.
175175

176176
Now let's try the following:
177177

@@ -190,9 +190,10 @@ Knowing whether a process is directly connected to a terminal can be useful in c
190190

191191
### See also
192192

193-
* TODO
194-
* ...chapter 3
195-
* .. anywhere else the process object is discussed
193+
* *Creating a Node.js WebSocket client* in the *There's More* section of *Communicating with WebSockets* in **Chapter 5 Wielding Web Protocols**
194+
* *Using the pipe method* in **Chapter 4 Using Streams**
195+
* *Getting symlink information* in the *There's More* section of *Fetching meta-data* in this chapter
196+
196197

197198
## Working with files
198199

@@ -201,8 +202,8 @@ to server side programming.
201202

202203
Node's `fs` module provides this ability.
203204

204-
In this recipe we'll learn how to read, write and append to files,
205-
synchronously then we'll follow up in the **There's More** section showing how to perform the same operations asynchronously and incrementally.
205+
In this recipe we'll learn how to read, write and append to files in a
206+
synchronous manner. In the **There's More** section we'll explore how to perform the same operations asynchronously and incrementally.
206207

207208
### Getting Ready
208209

@@ -211,7 +212,7 @@ We'll need a file to read.
211212
We can use the following to populate a file with 1MB of data:
212213

213214
```sh
214-
$ node -p "Buffer(1e6).toString()" > file.dat
215+
$ node -p "Buffer.allocUnsafe(1e6).toString()" > file.dat
215216
```
216217

217218
> #### Allocating Buffers ![](../info.png)
@@ -220,9 +221,11 @@ $ node -p "Buffer(1e6).toString()" > file.dat
220221
> *deallocated memory*, data in RAM that was previously discarded.
221222
> This means the buffer could contain anything.
222223
> From Node v6 and above, passing a number to `Buffer` is
223-
> deprecated, instead we should use `Buffer.allocUnsafe`
224+
> deprecated. Instead we use `Buffer.allocUnsafe`
224225
> to achieve the same effect, or just `Buffer.alloc` to have
225226
> a zero-filled buffer (but at the cost of slower instantiation).
227+
> To state the obvious, the file we generated for ourselves
228+
> (`file.dat`) should not be shared with anyone else.
226229
227230
We'll also want to create a source file, let's call it `null-byte-remover.js`.
228231

@@ -297,13 +300,15 @@ Finally, we use `fs.appendFileSync` to record the date and amount of bytes remov
297300

298301
### There's more
299302

303+
Let's explore asynchronous I/O.
304+
300305
#### Asynchronous file operations
301306

302307
Suppose we wanted some sort of feedback, to show that the process was doing something.
303308

304309
We could use an interval to write a dot to `process.stdout` every 10 milliseconds.
305310

306-
If we and add the following to top of the file:
311+
If we add the following to top of the file:
307312

308313
```js
309314
setInterval(() => process.stdout.write('.'), 10).unref()
@@ -397,14 +402,18 @@ chunk and stripped result is discarded, whilst the next chunk enters
397402
process memory. This all happens over multiple ticks of the event loop,
398403
allowing room for processing of the interval timer queue.
399404

400-
We'll be delving much deeper into Streams in **Chapter 3 Using Streams**,
405+
We'll be delving much deeper into Streams in **Chapter 4 Using Streams**,
401406
but for the time being we can see that `fs.createReadStream` and `fs.createWriteStream` are, more often that not, the most suitable way to read
402407
and write to files.
403408

404409
### See also
405410

406-
* TODO
407-
* chapter 3...etc
411+
* *Receiving POST Data* in **Chapter 5 Wielding Web protocols**
412+
* *Deploying a full system* in **Chapter 11 Deploying Node.js**
413+
* *Multipart POST uploads* in **Chapter 5 Wielding Web protocols**
414+
* *Processing big data* in **Chapter 4 Using Streams**
415+
* *Creating an SMTP server* in **Chapter 5 Wielding Web protocols**
416+
* *Fetching meta-data* in this chapter
408417

409418
## Fetching meta-data
410419

@@ -499,7 +508,7 @@ file in order to obtain information about it, then return that data, like so:
499508
```js
500509
function toMeta({file, dir}) {
501510
const stats = fs.statSync(path.join(dir, file))
502-
let {birthtime, ino, mode, nlink, size} = stats
511+
var {birthtime, ino, mode, nlink, size} = stats
503512
birthtime = birthtime.toUTCString()
504513
mode = mode.toString(8)
505514
size += 'B'
@@ -657,7 +666,7 @@ in bold, if it isn't we write a in a dulled down white color.
657666
Let's find out how to examine symlinks, check whether files exists and see how to
658667
actually alter file system metadata.
659668

660-
#### Getting symlink information
669+
#### Getting symlink information
661670

662671
There are other types of stat calls, one such call is `lstat` (the 'l' stands for link).
663672

@@ -672,7 +681,7 @@ First we'll modify the `toMeta` function to use `fs.lstatSync` instead of
672681
```js
673682
function toMeta({file, dir}) {
674683
const stats = fs.lstatSync(path.join(dir, file))
675-
let {birthtime, ino, mode, nlink, size} = stats
684+
var {birthtime, ino, mode, nlink, size} = stats
676685
birthtime = birthtime.toUTCString()
677686
mode = mode.toString(8)
678687
size += 'B'
@@ -793,9 +802,10 @@ exists(process.argv[2])
793802

794803
> #### Promises ![](../info.png)
795804
> For extra fun here (because the paradigm fits well in this case),
796-
> we used the ES2015 native `Promise` abstraction. Find out more about
797-
> promises at https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Promise
798-
805+
> we used the ES2015 native `Promise` abstraction. Find out more about promises at https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Promise
806+
> In general we tend to use a minimal subset of ES2015 (ES6) throughout so we can focus more
807+
> on using Node and less on syntax, avoiding either extra discourse or potential confusion.
808+
> We should also note Promises are currently a poor choice for implementing production server logic in Node, due to (standards specified) opaque behaviors (error swallowing, asynchronous stack unwinding) leading to difficulties in production root cause analysis.
799809
800810
Now if we run the following:
801811

@@ -827,7 +837,7 @@ If there's a problem accessing, an error will be logged, if not `null` will be l
827837

828838

829839
> #### Modes and Bitmasks ![](../info.png)
830-
> For more on `fs.access` see the docs at https://nodejs.org/api/fs.html#fs_fs_access_path_mode_callback, to learn about bitmasks check out https://abdulapopoola.com/2016/05/30/understanding-bit-masks/
840+
> For more on `fs.access` see the docs at https://nodejs.org/api/fs.html#fs_fs_access_path_mode_callback, to learn about bitmasks check out https://abdulapopoola.com/2016/05/30/understanding-bit-masks/
831841
832842

833843
#### Manipulating metadata
@@ -838,7 +848,7 @@ Let's create a small program that creates a file, sets the UID and GID to `nobod
838848

839849
```js
840850
const fs = require('fs')
841-
const {execSync} = require('child_process')
851+
const { execSync } = require('child_process')
842852

843853
const file = process.argv[2]
844854
if (!file) {
@@ -866,7 +876,7 @@ We used `fs.accessSync` to synchronously check for file existence, using a
866876
`try/catch` since `fs.accessSync` throws when a file does not exist.
867877

868878
> #### try/catch ![](../tip.png)
869-
> In this particular context, a try/catch is fine. However as a general rule we should avoid try/catch as much as possible. See [How to know when (not) to throw](http://www.nearform.com/nodecrunch/10-tips-coding-node-js-3-know-throw-2/) for more details.
879+
> In this particular context, a try/catch is fine. However as a general rule we should avoid try/catch when possible. While Node 6 and above successfully handle the performance implications of try/catch there are other points to be aware of. See [How to know when (not) to throw](http://www.nearform.com/nodecrunch/10-tips-coding-node-js-3-know-throw-2/) for more details.
870880
871881
If the file does not exist, we call our `makeIt` function.
872882

@@ -891,13 +901,14 @@ function makeIt() {
891901
This achieve the same result, but directly manages the file handle (an OS-level reference to the file).
892902

893903
We use `fs.openSync` to create the file and get a file descriptor (`fd`),
894-
then instead of fs.chmodSync and fs.chownSync both of which expect a file
904+
then instead of `fs.chmodSync` and `fs.chownSync` both of which expect a file
895905
path, we use `fs.fchmodSync` and `fs.fchownSync` which take a file descriptor.
896906

897907

898908
### See also
899909

900-
* TODO
910+
* *Watching files and directories* in this chapter
911+
* *Receiving POST Data* in **Chapter 5 Wielding Web protocols**
901912

902913
## Watching files and directories
903914

@@ -929,7 +940,7 @@ We'll also create a file to watch:
929940
$ echo "some content" > my-file.txt
930941
```
931942

932-
Finally we want to create a file called `watcher.js` (inside the `watching-files-and-directories` folder) and open it in our favourite editor.
943+
Finally we want to create a file called `watcher.js` (inside the `watching-files-and-directories` folder) and open it in our favorite editor.
933944

934945
### How to do it
935946

@@ -945,7 +956,7 @@ Next we'll set up some references:
945956
```js
946957
const interval = 5007
947958
const file = process.argv[2]
948-
let exists = false
959+
var exists = false
949960
```
950961

951962
Do a quick check to make sure we've been supplied a file:
@@ -1166,11 +1177,12 @@ mkdir my-subfolder
11661177

11671178
### See also
11681179

1169-
* Chapter 2, Fetching Metadata
1170-
* TODO - more
1180+
* *Fetching meta-data* in this chapter
1181+
* *Setting up a development environment* in **Chapter 10 Building Microservice systems**
11711182

11721183

11731184
## Communicating over sockets
1185+
11741186
One way to look at a socket is as a special file. Like a file it's a readable and writable data container. On some Operating Systems network sockets are literally a special type of file whereas on others the implementation is more abstract.
11751187

11761188
At any rate, the concept of a socket has changed our lives because it allows
@@ -1181,7 +1193,7 @@ In this recipe we'll build a TCP client and server.
11811193
### Getting Ready
11821194

11831195
Let's create two files `client.js` and `server.js` and open them in our
1184-
favourite editor.
1196+
favorite editor.
11851197

11861198
### How to do it
11871199

@@ -1294,7 +1306,7 @@ Let's learn a little more about sockets, and the different types of sockets that
12941306
#### `net` sockets are streams
12951307

12961308
Previous recipes in this chapter have alluded to streams,
1297-
we'll be studying these in depth in **Chapter 3 Using Streams**.
1309+
we'll be studying these in depth in **Chapter 4 Using Streams**.
12981310

12991311
However we would be remiss if we didn't mention that TCP sockets
13001312
implement the streams interface.
@@ -1440,13 +1452,12 @@ We'll notice that the server (like the client) no longer listens for a `close` e
14401452
this is because the sockets are bound to different ports so there's not way
14411453
(without a higher level protocol like TCP) of triggering a close from the other side.
14421454

1455+
14431456
### See also
14441457

1445-
* TODO
1446-
* Interfacing with standard I/O
1447-
* making clients and servers chapter
1448-
* streams chapter?
1449-
* wielding express
1450-
* getting hapi
1451-
* microservices chapter?
1458+
* *Interfacing with standard I/O* in this chapter
1459+
* *Setting up a development environment* in **Chapter 10 Building Microservice systems**
1460+
* *Using the pipe method* in **Chapter 4 Using Streams**
1461+
* *Decoupling I/O* in **Chapter 4 Using Streams**
1462+
* *Pattern Routing* in the *There's More* section of *Standardizing service boilerplate* in **Chapter 10 Building Microservice Systems**
14521463

0 commit comments

Comments
 (0)