-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathslides_node_basics.html
More file actions
425 lines (400 loc) · 28.2 KB
/
slides_node_basics.html
File metadata and controls
425 lines (400 loc) · 28.2 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Slides for
Node.js Basics — Ruby on Rails Guides
</title>
<link rel="stylesheet" type="text/css" href="stylesheets/style.css" data-turbo-track="reload">
<link rel="stylesheet" type="text/css" href="stylesheets/print.css" media="print">
<link rel="stylesheet" type="text/css" href="stylesheets/highlight.css" data-turbo-track="reload">
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="stylesheets/reset.css">
<link rel="stylesheet" href="stylesheets/reveal.css">
<link rel="stylesheet" href="stylesheets/myslide.css" id="theme">
<link rel="stylesheet" href="stylesheets/code.css">
<script src="javascripts/clipboard.js" data-turbo-track="reload"></script>
<script src="javascripts/slides.js" data-turbo-track="reload"></script>
</head>
<body>
<div class="reveal">
<!-- Any section element inside of this container is displayed as a slide -->
<div class="slides">
<section>
<h1>Node.js Basics</h1><p>Node.js is just a javascript interpreter
with a very small library added. With node
you build your web server from scratch.</p><p>After working through this guide you should</p>
<ul>
<li>know how to install libraries for node.js with npm</li>
<li>be able to write a simple web server</li>
</ul>
<p><small>Slides - use arrow keys to navigate, esc to return to page view, f for fullscreen</small></p>
</section>
<section><a class='slide_break' href='node_basics.html#slide-0'>▻</a>
<h2 id="what-is-node-js-questionmark"><a class="anchorlink" href="#what-is-node-js-questionmark"><span>1</span> What is node.js?</a></h2><p>Node.js was originally written by Ryan Dahl in 2009
as a combination of two pieces of software that
already existed:</p>
<ul>
<li>the google <a href="https://v8.dev/">javascript interpreter v8</a> - <a href="https://github.com/v8/v8">code</a></li>
<li>a library for asyncronous programming <a href="https://libuv.org/">libuv</a> - <a href="https://github.com/libuv/libuv/">code</a></li>
</ul>
<p>To this he added a library written in Javascript.
In 2010 npm was added as a package manager for
Javascript Libraries.</p></section>
<section><a class='slide_break' href='node_basics.html#slide-1'>▻</a>
<h3 id="how-big-is-node-js-questionmark"><a class="anchorlink" href="#how-big-is-node-js-questionmark"><span>1.1</span> How big is node.js?</a></h3><p><img src="images/what-is-node.svg" alt=""></p><p>In 2023, according to <a href="https://www.openhub.net/p/node/analyses/latest/languages_summary">openhub</a>
the node projects consist of:</p>
<ul>
<li>the node library: 1,6 millions lines of Javascript code</li>
<li>node bindings: 2,5 millions lines of C code</li>
<li>v8: 2,3 millions lines of code written in c++</li>
<li>libuv: 100.000 lines of C code</li>
</ul>
</section>
<section><a class='slide_break' href='node_basics.html#slide-2'>▻</a>
<h3 id="node-version-manager"><a class="anchorlink" href="#node-version-manager"><span>1.2</span> Node Version Manager</a></h3><p>Node Versions change fast. The node version manager (nvm) makes
it easy to switch between versions:</p><div class="interstitial code">
<pre><code class="highlight shell"><span class="nv">$ </span>nvm use 16.19
Now using <span class="nb">node </span>v16.19.0 <span class="o">(</span>npm v8.19.3<span class="o">)</span>
<span class="nv">$ </span>nvm use stable
Now using <span class="nb">node </span>v20.2.0 <span class="o">(</span>npm v9.6.6
</code></pre>
<button class="clipboard-button" data-clipboard-text="$ nvm use 16.19
Now using node v16.19.0 (npm v8.19.3)
$ nvm use stable
Now using node v20.2.0 (npm v9.6.6
">Copy</button>
</div>
<p>An alternative would be to use <a href="https://github.com/jdxcode/rtx">rtx</a>, which
can also handle other languages like ruby, php, python.</p></section>
<section><a class='slide_break' href='node_basics.html#slide-3'>▻</a>
<h3 id="hello-node"><a class="anchorlink" href="#hello-node"><span>1.3</span> Hello Node</a></h3><p>You write your program in Javascript:</p><div class="interstitial code">
<pre><code class="highlight javascript"><span class="nx">console</span><span class="p">.</span><span class="nf">log</span><span class="p">(</span><span class="dl">"</span><span class="s2">Hello Node</span><span class="dl">"</span><span class="p">);</span>
</code></pre>
<button class="clipboard-button" data-clipboard-text="console.log("Hello Node");
">Copy</button>
</div>
<p>and run it with the <code>node</code> command:</p><div class="interstitial code">
<pre><code class="highlight shell"><span class="nv">$ </span><span class="nb">node </span>hello.js
Hello Node
</code></pre>
<button class="clipboard-button" data-clipboard-text="$ node hello.js
Hello Node
">Copy</button>
</div>
<p>You can also use node interactively:</p><div class="interstitial code">
<pre><code class="highlight shell"><span class="nv">$ </span><span class="nb">node</span>
<span class="o">></span> console.log<span class="o">(</span><span class="s2">"Hello Node"</span><span class="o">)</span><span class="p">;</span>
Hello Node
undefined
<span class="o">></span> 1+2
3
<span class="o">></span> <span class="o">[</span>CTRL]-[D]
</code></pre>
<button class="clipboard-button" data-clipboard-text="$ node
> console.log("Hello Node");
Hello Node
undefined
> 1+2
3
> [CTRL]-[D]
">Copy</button>
</div>
</section>
<section><a class='slide_break' href='node_basics.html#slide-4'>▻</a>
<h3 id="hello-web"><a class="anchorlink" href="#hello-web"><span>1.4</span> Hello Web</a></h3><p>To create a very simple Webserver we can use the package <code>http</code>:</p><div class="interstitial code">
<pre><code class="highlight plaintext">import * as http from 'http';
const hostname = '127.0.0.1';
const port = 3000;
const server = http.createServer((req, res) => {
res.statusCode = 200;
res.setHeader('Content-Type', 'text/plain');
res.end('Hello Web\n');
});
server.listen(port, hostname, () => {
console.log(`Server running at http://${hostname}:${port}/`);
});
</code></pre>
<button class="clipboard-button" data-clipboard-text="import * as http from 'http';
const hostname = '127.0.0.1';
const port = 3000;
const server = http.createServer((req, res) => {
res.statusCode = 200;
res.setHeader('Content-Type', 'text/plain');
res.end('Hello Web\n');
});
server.listen(port, hostname, () => {
console.log(`Server running at http://${hostname}:${port}/`);
});
">Copy</button>
</div>
<div class="interstitial code">
<pre><code class="highlight shell"><span class="nv">$ </span><span class="nb">node </span>app.js
Server running at http://127.0.0.1:3000/
</code></pre>
<button class="clipboard-button" data-clipboard-text="$ node app.js
Server running at http://127.0.0.1:3000/
">Copy</button>
</div>
</section>
<section><a class='slide_break' href='node_basics.html#slide-5'>▻</a>
<h2 id="packages"><a class="anchorlink" href="#packages"><span>2</span> Packages</a></h2><p>Node had it's own package system called CommonJS, using the keyword <code>require</code>.
Since node 13 you can also use ECMAScript modules with the keyword <code>import</code>.</p><p>Use the type field in <code>package.json</code> to switch to ECMAScript modules,
or use extension <code>.cjs</code> for CommonJS and <code>.mjs</code> for modules.</p><div class="interstitial code">
<pre><code class="highlight plaintext">// package.json
{
"type": "module"
}
</code></pre>
<button class="clipboard-button" data-clipboard-text="// package.json
{
"type": "module"
}
">Copy</button>
</div>
<p>Use barewords to import packages from <code>node_modules</code>, and relative
paths for your own source files:</p><div class="interstitial code">
<pre><code class="highlight plaintext">import * as http from 'http';
import * as config from './config/index.js`;
</code></pre>
<button class="clipboard-button" data-clipboard-text="import * as http from 'http';
import * as config from './config/index.js`;
">Copy</button>
</div>
<p><code>npm</code> was the first package manager for node.js. Today
there are many alternatives, from <code>yarn</code> to <code>pnpm</code>.</p></section>
<section><a class='slide_break' href='node_basics.html#slide-6'>▻</a>
<h2 id="the-javascript-event-loop"><a class="anchorlink" href="#the-javascript-event-loop"><span>3</span> The javascript Event Loop</a></h2><p>You have worked with Javascript and asyncronous programming before.</p><div class="interstitial code">
<pre><code class="highlight javascript"><span class="kd">function</span> <span class="nf">f</span><span class="p">()</span> <span class="p">{</span>
<span class="nx">console</span><span class="p">.</span><span class="nf">log</span><span class="p">(</span><span class="dl">"</span><span class="s2">foo</span><span class="dl">"</span><span class="p">);</span>
<span class="nf">setTimeout</span><span class="p">(</span><span class="nx">g</span><span class="p">,</span> <span class="mi">0</span><span class="p">);</span>
<span class="nx">console</span><span class="p">.</span><span class="nf">log</span><span class="p">(</span><span class="dl">"</span><span class="s2">baz</span><span class="dl">"</span><span class="p">);</span>
<span class="nf">h</span><span class="p">();</span>
<span class="p">}</span>
<span class="kd">function</span> <span class="nf">g</span><span class="p">()</span> <span class="p">{</span>
<span class="nx">console</span><span class="p">.</span><span class="nf">log</span><span class="p">(</span><span class="dl">"</span><span class="s2">bar</span><span class="dl">"</span><span class="p">);</span>
<span class="p">}</span>
<span class="kd">function</span> <span class="nf">h</span><span class="p">()</span> <span class="p">{</span>
<span class="nx">console</span><span class="p">.</span><span class="nf">log</span><span class="p">(</span><span class="dl">"</span><span class="s2">blix</span><span class="dl">"</span><span class="p">);</span>
<span class="p">}</span>
<span class="nx">el</span><span class="p">.</span><span class="nf">addEventListener</span><span class="p">(</span><span class="dl">"</span><span class="s2">click</span><span class="dl">"</span><span class="p">,</span> <span class="nx">f</span><span class="p">);</span>
</code></pre>
<button class="clipboard-button" data-clipboard-text="function f() {
console.log("foo");
setTimeout(g, 0);
console.log("baz");
h();
}
function g() {
console.log("bar");
}
function h() {
console.log("blix");
}
el.addEventListener("click", f);
">Copy</button>
</div>
<p>If this code runs in the browser, the output on the console will be:</p>
<ol>
<li>foo</li>
<li>baz</li>
<li>blix</li>
<li>bar</li>
</ol>
</section>
<section><a class='slide_break' href='node_basics.html#slide-7'>▻</a>
<h2 id="classic-processing-model"><a class="anchorlink" href="#classic-processing-model"><span>4</span> Classic Processing Model</a></h2><p>PHP and Ruby on Rails have the same basic processing model. It is either
implemented with threads or with processes.</p>
<ul>
<li>when the webserver first starts, a number of threads are started</li>
<li>when a http comes in, it is handled by one thread from beginning to end
<ul>
<li>the thread will probably spend some time waiting for slow I/O, like a database response</li>
</ul></li>
</ul>
<p>Apache comes with a module <code>server_status</code> that displays the
processes/threads and their status on a webpage. Here an example:</p><p><img src="/images/apache-server-status.png" alt="Apache Server Status"></p><p>As you can see the server is running in <code>prefork</code> mode: when
the server is started it forks a certain number of worker processes,
but it can also fork additional worker process later on.</p><p>Currently 56 requests are being processed, 8 worker processes are
idle, and there are a lot of additional slots for additional worker
processes.</p></section>
<section><a class='slide_break' href='node_basics.html#slide-8'>▻</a>
<h3 id="syncronous-processing"><a class="anchorlink" href="#syncronous-processing"><span>4.1</span> Syncronous Processing</a></h3><p>Using syncronous I/O the program code will look something like this:</p><div class="interstitial code">
<pre><code class="highlight ruby"><span class="n">file</span> <span class="o">=</span> <span class="no">File</span><span class="p">.</span><span class="nf">open</span><span class="p">(</span><span class="n">file_name</span><span class="p">,</span> <span class="s2">"r"</span><span class="p">)</span> <span class="c1"># takes a long time, thread has to wait</span>
<span class="n">data</span> <span class="o">=</span> <span class="n">file</span><span class="p">.</span><span class="nf">read</span> <span class="c1"># takes a very long time, thread has to wait</span>
<span class="n">file</span><span class="p">.</span><span class="nf">close</span>
</code></pre>
<button class="clipboard-button" data-clipboard-text="file = File.open(file_name, "r") # takes a long time, thread has to wait
data = file.read # takes a very long time, thread has to wait
file.close
">Copy</button>
</div>
<p>As each thread comes with a fixed overhead of memory demand, you
can only start so many threads on a given machine. You configure this
in the web server configuration, e.g.</p>
<ul>
<li>when running PHP with apache and PHP-FPM with the configuration directives <code>pm.max_children</code>, ``<code>pm.start_servers</code>,<code>pm.min_spare_servers</code>,<code>pm.max_spare_servers</code>, see <a href="https://secure.php.net/manual/de/install.fpm.configuration.php">php.net</a></li>
<li>when running Rails with Passenger with the configuration directives <code>PassengerMinInstances</code> und <code>PassengerMaxPoolSize</code>, see <a href="https://www.phusionpassenger.com/library/config/apache/optimization/">phusionpassenger.com</a></li>
</ul>
</section>
<section><a class='slide_break' href='node_basics.html#slide-9'>▻</a>
<h2 id="node-js-processing-model"><a class="anchorlink" href="#node-js-processing-model"><span>5</span> Node.js processing model</a></h2><p>Node has a completely different model:</p>
<ul>
<li>there is one thread running the javascript event loop</li>
<li>if the thread is free, it picks up the next event from the event queue. this might be a new http request</li>
<li>all I/O is done asynchronosly: the main thread hands off the request to the database to a new, separate thread from a thread pool. When the request is done, and the data is available, this is added as a new event to the event queue</li>
<li>after starting an asynchronos thread, the main thread immediately contious working</li>
</ul>
<p><img src="images/nodejs-eventloop.jpg" alt="node.js event loop"></p><p>(diagram by <a href="https://twitter.com/RichOnTheWeb/status/494959181871316992">@RichOnTheWeb</a>)</p></section>
<section><a class='slide_break' href='node_basics.html#slide-10'>▻</a>
<h3 id="asyncronous-with-callbacks"><a class="anchorlink" href="#asyncronous-with-callbacks"><span>5.1</span> Asyncronous with callbacks</a></h3><p>Doing asyncronous I/O is implemented using callbacks in Javascript, and will look
something like this:</p><div class="interstitial code">
<pre><code class="highlight javascript"><span class="k">import</span> <span class="p">{</span> <span class="nx">readFile</span> <span class="p">}</span> <span class="k">from</span> <span class="dl">'</span><span class="s1">node:fs</span><span class="dl">'</span><span class="p">;</span>
<span class="nx">console</span><span class="p">.</span><span class="nf">log</span><span class="p">(</span><span class="dl">"</span><span class="s2">first</span><span class="dl">"</span><span class="p">);</span>
<span class="nf">readFile</span><span class="p">(</span><span class="nx">file_name</span><span class="p">,</span> <span class="p">(</span><span class="nx">err</span><span class="p">,</span> <span class="nx">data</span><span class="p">)</span> <span class="o">=></span> <span class="p">{</span>
<span class="k">if </span><span class="p">(</span><span class="nx">err</span><span class="p">)</span> <span class="k">throw</span> <span class="nx">err</span><span class="p">;</span>
<span class="nx">console</span><span class="p">.</span><span class="nf">log</span><span class="p">(</span><span class="dl">"</span><span class="s2">... much later, third</span><span class="dl">"</span><span class="p">);</span>
<span class="nx">console</span><span class="p">.</span><span class="nf">log</span><span class="p">(</span><span class="nx">data</span><span class="p">);</span>
<span class="p">});</span>
<span class="nx">console</span><span class="p">.</span><span class="nf">log</span><span class="p">(</span><span class="dl">"</span><span class="s2">second</span><span class="dl">"</span><span class="p">);</span>
</code></pre>
<button class="clipboard-button" data-clipboard-text="import { readFile } from 'node:fs';
console.log("first");
readFile(file_name, (err, data) => {
if (err) throw err;
console.log("... much later, third");
console.log(data);
});
console.log("second");
">Copy</button>
</div>
<p>If this is the whole program, the main thread will become free after
printing out <code>second</code>. It will pick up something else to do from
the event queue. Much later, when the data from the file has been
loaded, it will find the callback funktion on the event queue, and
finally reach <code>third</code>.</p></section>
<section><a class='slide_break' href='node_basics.html#slide-11'>▻</a>
<h3 id="asyncronous-with-async-await"><a class="anchorlink" href="#asyncronous-with-async-await"><span>5.2</span> Asyncronous with async await</a></h3><p>Node also offers the use of promises or async await:</p><div class="interstitial code">
<pre><code class="highlight javascript"><span class="k">import</span> <span class="p">{</span> <span class="nx">readFile</span> <span class="p">}</span> <span class="k">from</span> <span class="dl">'</span><span class="s1">node:fs/promises</span><span class="dl">'</span><span class="p">;</span>
<span class="kd">const</span> <span class="nx">filePath</span> <span class="o">=</span> <span class="dl">'</span><span class="s1">package.json</span><span class="dl">'</span><span class="p">;</span>
<span class="kd">const</span> <span class="nx">contents</span> <span class="o">=</span> <span class="k">await</span> <span class="nf">readFile</span><span class="p">(</span><span class="nx">filePath</span><span class="p">,</span> <span class="p">{</span> <span class="na">encoding</span><span class="p">:</span> <span class="dl">'</span><span class="s1">utf8</span><span class="dl">'</span> <span class="p">});</span>
</code></pre>
<button class="clipboard-button" data-clipboard-text="import { readFile } from 'node:fs/promises';
const filePath = 'package.json';
const contents = await readFile(filePath, { encoding: 'utf8' });
">Copy</button>
</div>
<p>The main thread will become free at <code>await</code>. It will pick up something else to do from
the event queue. Much later, when the data from the file has been
loaded, the program will continue with assigning the data to the constant <code>contents</code>.</p></section>
<section><a class='slide_break' href='node_basics.html#slide-12'>▻</a>
<h3 id="complex-example"><a class="anchorlink" href="#complex-example"><span>5.3</span> Complex Example</a></h3><p><img src="images/node-example-code@2x.png" alt=""></p><p><a href="https://gitlab.mediacube.at/-/snippets/66">source</a></p><p><img src="images/node-example-diagram.png" alt=""></p></section>
<section><a class='slide_break' href='node_basics.html#slide-13'>▻</a>
<h3 id="io-bound-vs-cpu-bound"><a class="anchorlink" href="#io-bound-vs-cpu-bound"><span>5.4</span> IO-bound vs. CPU-bound</a></h3><p>Describes two kinds of performance bottlenecks</p>
<ul>
<li>An I/O-bound application waits most of the time for network, filesystem and database.
Running on a faster CPU would not help.</li>
<li>A CPU-bound application spends most of the time using the CPU, running on a faster CPU would help.</li>
</ul>
<p>The node process model helps with I/O-bound applications: If your app is I/O-bound, the event loop will be able to serve many requests, while other threads handling the acutal I/O will run on other kernels</p><p>If one aspect of your app is CPU-bound it will monopolize that kernel,
(other) requests cannot be served. Therefore node and is not well suited for CPU
bound applications.</p></section>
<section><a class='slide_break' href='node_basics.html#slide-14'>▻</a>
<h2 id="streams"><a class="anchorlink" href="#streams"><span>6</span> Streams</a></h2><p>Streams are a basic bilding block of a node application.
Without streams, you have to read the whole file before
you can send it:</p><div class="interstitial code">
<pre><code class="highlight javascript"><span class="k">import</span> <span class="p">{</span> <span class="nx">readFile</span> <span class="p">}</span> <span class="k">from</span> <span class="dl">'</span><span class="s1">node:fs/promises</span><span class="dl">'</span><span class="p">;</span>
<span class="nx">router</span><span class="p">.</span><span class="nf">get</span><span class="p">(</span><span class="dl">'</span><span class="s1">/all</span><span class="dl">'</span><span class="p">,</span> <span class="kd">function</span><span class="p">(</span><span class="nx">req</span><span class="p">,</span> <span class="nx">res</span><span class="p">,</span> <span class="nx">next</span><span class="p">)</span> <span class="p">{</span>
<span class="kd">const</span> <span class="nx">contents</span> <span class="o">=</span> <span class="k">await</span> <span class="nf">readFile</span><span class="p">(</span><span class="nx">path</span><span class="p">.</span><span class="nf">join</span><span class="p">(</span><span class="nx">__dirname</span><span class="p">,</span> <span class="dl">'</span><span class="s1">../giant.html</span><span class="dl">'</span><span class="p">),</span> <span class="p">{</span> <span class="na">encoding</span><span class="p">:</span> <span class="dl">'</span><span class="s1">utf8</span><span class="dl">'</span> <span class="p">});</span>
<span class="nx">res</span><span class="p">.</span><span class="nx">statusCode</span> <span class="o">=</span> <span class="mi">200</span><span class="p">;</span>
<span class="nx">res</span><span class="p">.</span><span class="nf">setHeader</span><span class="p">(</span><span class="dl">'</span><span class="s1">Content-Type</span><span class="dl">'</span><span class="p">,</span> <span class="dl">'</span><span class="s1">text/html</span><span class="dl">'</span><span class="p">);</span>
<span class="nx">res</span><span class="p">.</span><span class="nf">end</span><span class="p">(</span><span class="nx">contents</span><span class="p">);</span>
<span class="p">});</span>
</code></pre>
<button class="clipboard-button" data-clipboard-text="import { readFile } from 'node:fs/promises';
router.get('/all', function(req, res, next) {
const contents = await readFile(path.join(__dirname, '../giant.html'), { encoding: 'utf8' });
res.statusCode = 200;
res.setHeader('Content-Type', 'text/html');
res.end(contents);
});
">Copy</button>
</div>
</section>
<section><a class='slide_break' href='node_basics.html#slide-15'>▻</a>
<h3 id="using-streams"><a class="anchorlink" href="#using-streams"><span>6.1</span> Using Streams</a></h3><p>Better: connect a stream reading the file to the stream
that is the HTTP response:</p><div class="interstitial code">
<pre><code class="highlight javascript"><span class="k">import</span> <span class="p">{</span> <span class="nx">createReadStream</span> <span class="p">}</span> <span class="k">from</span> <span class="dl">'</span><span class="s1">node:fs</span><span class="dl">'</span><span class="p">;</span>
<span class="nx">router</span><span class="p">.</span><span class="nf">get</span><span class="p">(</span><span class="dl">'</span><span class="s1">/stream</span><span class="dl">'</span><span class="p">,</span> <span class="kd">function</span><span class="p">(</span><span class="nx">req</span><span class="p">,</span> <span class="nx">res</span><span class="p">,</span> <span class="nx">next</span><span class="p">)</span> <span class="p">{</span>
<span class="kd">let</span> <span class="nx">readStream</span> <span class="o">=</span> <span class="nx">fs</span><span class="p">.</span><span class="nf">createReadStream</span><span class="p">(</span><span class="nx">path</span><span class="p">.</span><span class="nf">join</span><span class="p">(</span><span class="nx">__dirname</span><span class="p">,</span> <span class="dl">'</span><span class="s1">../giant.html</span><span class="dl">'</span><span class="p">));</span>
<span class="nx">res</span><span class="p">.</span><span class="nf">setHeader</span><span class="p">(</span><span class="dl">'</span><span class="s1">Content-Type</span><span class="dl">'</span><span class="p">,</span> <span class="dl">'</span><span class="s1">text/html</span><span class="dl">'</span><span class="p">);</span>
<span class="nx">readStream</span><span class="p">.</span><span class="nf">pipe</span><span class="p">(</span><span class="nx">res</span><span class="p">);</span>
<span class="p">});</span>
</code></pre>
<button class="clipboard-button" data-clipboard-text="import { createReadStream } from 'node:fs';
router.get('/stream', function(req, res, next) {
let readStream = fs.createReadStream(path.join(__dirname, '../giant.html'));
res.setHeader('Content-Type', 'text/html');
readStream.pipe(res);
});
">Copy</button>
</div>
<p><code>.pipe()</code> takes care of listening for 'data' and 'end' events from the fs.createReadStream().
This code is not only cleaner, but now the giant.html file will be written to clients one chunk
at a time immediately as they are received from the disk.</p><p>Using <code>.pipe()</code> has other benefits too, like handling backpressure automatically
so that node won't buffer chunks into memory needlessly when the remote
client is on a really slow or high-latency connection.</p><p>You can also stream <a href="https://github.com/brianc/node-postgres/tree/master/packages/pg-query-stream#pg-query-stream">data from the database</a>.</p></section>
<section><a class='slide_break' href='node_basics.html#slide-16'>▻</a>
<h2 id="see-also"><a class="anchorlink" href="#see-also"><span>7</span> See Also</a></h2>
<ul>
<li><a href="https://nodejs.org/en/docs/guides/anatomy-of-an-http-transaction/">Node Guide: HTTP</a></li>
<li><a href="https://nodejs.org/en/docs/guides/event-loop-timers-and-nexttick/">nexttick</a></li>
<li><a href="https://stackoverflow.com/questions/19822668/what-exactly-is-a-node-js-event-loop-tick">Event Loop Implementation</a></li>
<li><a href="http://docs.libuv.org/en/v1.x/threadpool.html">set the event pool size process.env.UV_THREADPOOL_SIZE</a></li>
<li><a href="https://github.com/nodejs/node/blob/278a9267ec41f37e6b7dda876c417945d7725973/src/node.cc#L3964-L3965">V8 needs 4 threads</a></li>
</ul>
</div></section>
</div>
</div>
<!-- End slides. -->
<!-- Required JS files. -->
<script src="javascripts/reveal.js"></script>
<script src="javascripts/search.js"></script>
<script src="javascripts/markdown.js"></script>
<script>
// Also available as an ES module, see:
// https://revealjs.com/initialization/
Reveal.initialize({
controls: false,
progress: true,
center: false,
hash: true,
// The "normal" size of the presentation, aspect ratio will
// be preserved when the presentation is scaled to fit different
// resolutions. Can be specified using percentage units.
width: 1000,
height: 600,
disableLayout: false,
// Factor of the display size that should remain empty around
// the content
margin: 0.05,
// Bounds for smallest/largest possible scale to apply to content
minScale: 0.2,
maxScale: 10.0,
keyboard: {
27: () => {
// do something custom when ESC is pressed
var new_url = window.location.pathname.replace('slides_', '') + window.location.hash.replace('/','slide-');
window.location = new_url;
},
191: 'toggleHelp',
13: 'next', // go to the next slide when the ENTER key is pressed
},
// Learn about plugins: https://revealjs.com/plugins/
plugins: [ RevealSearch, RevealMarkdown ]
});
</script>
</body>
</html>