Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ For example, say you schedule a timeout to execute after a 100 ms
threshold, then your script starts asynchronously reading a file which
takes 95 ms:

```js
```cjs
const fs = require('node:fs');

function someAsyncOperation(callback) {
Expand All @@ -137,6 +137,33 @@ someAsyncOperation(() => {
});
```

```mjs
import fs from 'node:fs';

function someAsyncOperation(callback) {
// Assume this takes 95ms to complete
fs.readFile('/path/to/file', callback);
}

const timeoutScheduled = Date.now();

setTimeout(() => {
const delay = Date.now() - timeoutScheduled;

console.log(`${delay}ms have passed since I was scheduled`);
}, 100);

// do someAsyncOperation which takes 95 ms to complete
someAsyncOperation(() => {
const startCallback = Date.now();

// do something that will take 10ms...
while (Date.now() - startCallback < 10) {
// do nothing
}
});
```

When the event loop enters the **poll** phase, it has an empty queue
(`fs.readFile()` has not completed), so it will wait for the number of ms
remaining until the soonest timer's threshold is reached. While it is
Expand Down Expand Up @@ -259,7 +286,7 @@ timeout
However, if you move the two calls within an I/O cycle, the immediate
callback is always executed first:

```js
```cjs
// timeout_vs_immediate.js
const fs = require('node:fs');

Expand All @@ -273,6 +300,20 @@ fs.readFile(__filename, () => {
});
```

```mjs
// timeout_vs_immediate.js
import fs from 'node:fs';

fs.readFile(import.meta.filename, () => {
setTimeout(() => {
console.log('timeout');
}, 0);
setImmediate(() => {
console.log('immediate');
});
});
```

```bash
$ node timeout_vs_immediate.js
immediate
Expand Down Expand Up @@ -453,7 +494,7 @@ allowing the connection event to be fired before the listening event.
Another example is extending an `EventEmitter` and emitting an
event from within the constructor:

```js
```cjs
const EventEmitter = require('node:events');

class MyEmitter extends EventEmitter {
Expand All @@ -469,13 +510,30 @@ myEmitter.on('event', () => {
});
```

```mjs
import EventEmitter from 'node:events';

class MyEmitter extends EventEmitter {
constructor() {
super();
this.emit('event');
}
}

const myEmitter = new MyEmitter();
myEmitter.on('event', () => {
console.log('an event occurred!');
});
```

You can't emit an event from the constructor immediately

because the script will not have processed to the point where the user
assigns a callback to that event. So, within the constructor itself,
you can use `process.nextTick()` to set a callback to emit the event
after the constructor has finished, which provides the expected results:

```js
```cjs
const EventEmitter = require('node:events');

class MyEmitter extends EventEmitter {
Expand All @@ -495,5 +553,25 @@ myEmitter.on('event', () => {
});
```

```mjs
import EventEmitter from 'node:events';

class MyEmitter extends EventEmitter {
constructor() {
super();

// use nextTick to emit the event once a handler is assigned
process.nextTick(() => {
this.emit('event');
});
}
}

const myEmitter = new MyEmitter();
myEmitter.on('event', () => {
console.log('an event occurred!');
});
```

[libuv]: https://libuv.org/
[REPL]: https://nodejs.org/api/repl.html#repl_repl
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ How do you handle errors with callbacks? One very common strategy is to use what

If there is no error, the object is `null`. If there is an error, it contains some description of the error and other information.

```js
```cjs
const fs = require('node:fs');

fs.readFile('/file.json', (err, data) => {
Expand All @@ -111,6 +111,21 @@ fs.readFile('/file.json', (err, data) => {
});
```

```mjs
import fs from 'node:fs';

fs.readFile('/file.json', (err, data) => {
if (err) {
// handle error
console.log(err);
return;
}

// no errors, process data
console.log(data);
});
```

### The problem with callbacks

Callbacks are great for simple cases!
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,15 +39,21 @@ execute **asynchronously**.

Using the File System module as an example, this is a **synchronous** file read:

```js
```cjs
const fs = require('node:fs');

const data = fs.readFileSync('/file.md'); // blocks here until file is read
```

```mjs
import fs from 'node:fs';

const data = fs.readFileSync('/file.md'); // blocks here until file is read
```

And here is an equivalent **asynchronous** example:

```js
```cjs
const fs = require('node:fs');

fs.readFile('/file.md', (err, data) => {
Expand All @@ -57,6 +63,16 @@ fs.readFile('/file.md', (err, data) => {
});
```

```mjs
import fs from 'node:fs';

fs.readFile('/file.md', (err, data) => {
if (err) {
throw err;
}
});
```

The first example appears simpler than the second but has the disadvantage of
the second line **blocking** the execution of any additional JavaScript until
the entire file is read. Note that in the synchronous version if an error is
Expand All @@ -66,17 +82,25 @@ shown.

Let's expand our example a little bit:

```js
```cjs
const fs = require('node:fs');

const data = fs.readFileSync('/file.md'); // blocks here until file is read
console.log(data);
moreWork(); // will run after console.log
```

```mjs
import fs from 'node:fs';

const data = fs.readFileSync('/file.md'); // blocks here until file is read
console.log(data);
moreWork(); // will run after console.log
```

And here is a similar, but not equivalent asynchronous example:

```js
```cjs
const fs = require('node:fs');

fs.readFile('/file.md', (err, data) => {
Expand All @@ -89,6 +113,19 @@ fs.readFile('/file.md', (err, data) => {
moreWork(); // will run before console.log
```

```mjs
import fs from 'node:fs';

fs.readFile('/file.md', (err, data) => {
if (err) {
throw err;
}

console.log(data);
});
moreWork(); // will run before console.log
```

In the first example above, `console.log` will be called before `moreWork()`. In
the second example `fs.readFile()` is **non-blocking** so JavaScript execution
can continue and `moreWork()` will be called first. The ability to run
Expand Down Expand Up @@ -118,7 +155,7 @@ threads may be created to handle concurrent work.
There are some patterns that should be avoided when dealing with I/O. Let's look
at an example:

```js
```cjs
const fs = require('node:fs');

fs.readFile('/file.md', (err, data) => {
Expand All @@ -131,12 +168,25 @@ fs.readFile('/file.md', (err, data) => {
fs.unlinkSync('/file.md');
```

```mjs
import fs from 'node:fs';

fs.readFile('/file.md', (err, data) => {
if (err) {
throw err;
}

console.log(data);
});
fs.unlinkSync('/file.md');
```

In the above example, `fs.unlinkSync()` is likely to be run before
`fs.readFile()`, which would delete `file.md` before it is actually read. A
better way to write this, which is completely **non-blocking** and guaranteed to
execute in the correct order is:

```js
```cjs
const fs = require('node:fs');

fs.readFile('/file.md', (readFileErr, data) => {
Expand All @@ -154,6 +204,24 @@ fs.readFile('/file.md', (readFileErr, data) => {
});
```

```mjs
import fs from 'node:fs';

fs.readFile('/file.md', (readFileErr, data) => {
if (readFileErr) {
throw readFileErr;
}

console.log(data);

fs.unlink('/file.md', unlinkErr => {
if (unlinkErr) {
throw unlinkErr;
}
});
});
```

The above places a **non-blocking** call to `fs.unlink()` within the callback of
`fs.readFile()` which guarantees the correct order of operations.

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ Because this method is invoked post-initialization, the setting of startup-relat
PORT=1234
```

```js
```cjs
const { loadEnvFile } = require('node:process');

// Loads environment variables from the default .env file
Expand All @@ -89,9 +89,23 @@ loadEnvFile();
console.log(process.env.PORT); // Logs '1234'
```

```mjs
import { loadEnvFile } from 'node:process';

// Loads environment variables from the default .env file
loadEnvFile();

console.log(process.env.PORT); // Logs '1234'
```

You can also specify a custom path:

```js
```cjs
const { loadEnvFile } = require('node:process');
loadEnvFile('./config/.env');
```

```mjs
import { loadEnvFile } from 'node:process';
loadEnvFile('./config/.env');
```
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,14 @@ const heapSizeInGB = heap_size_limit / (1024 * 1024 * 1024);
console.log(`${heapSizeInGB} GB`);
```

```mjs
import v8 from 'node:v8';
const { heap_size_limit } = v8.getHeapStatistics();
const heapSizeInGB = heap_size_limit / (1024 * 1024 * 1024);

console.log(`${heapSizeInGB} GB`);
```

This will output the maximum heap size in gigabytes, which is based on your system's available memory.

### The Stack
Expand Down
Loading