-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
12 changed files
with
352 additions
and
0 deletions.
There are no files selected for viewing
11 changes: 11 additions & 0 deletions
11
...ol-flow-patterns-with-promises-and-async-await/01-delay-with-promises/README.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
# 01-delay-with-promises | ||
|
||
This sample demonstrates how to use the `Promise` constructor to create a new `Promise` from scratch. | ||
|
||
## Run | ||
|
||
To run the example launch: | ||
|
||
```bash | ||
node index.js | ||
``` |
13 changes: 13 additions & 0 deletions
13
...onous-control-flow-patterns-with-promises-and-async-await/01-delay-with-promises/index.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
function delay(milliseconds) { | ||
return new Promise((resolve, _reject) => { | ||
setTimeout(() => { | ||
resolve(Date.now()) | ||
}, milliseconds) | ||
}) | ||
} | ||
|
||
console.log(`Delaying... (${Date.now()})`) | ||
|
||
delay(1000).then(newDate => { | ||
console.log(`Done (${newDate})`) | ||
}) |
15 changes: 15 additions & 0 deletions
15
...s-control-flow-patterns-with-promises-and-async-await/01-delay-with-promises/package.json
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
{ | ||
"name": "01-delay-with-promises", | ||
"version": "1.0.0", | ||
"description": "This sample demonstrates how to use the `Promise` constructor to create a new `Promise` from scratch", | ||
"type": "module", | ||
"scripts": {}, | ||
"engines": { | ||
"node": ">=22" | ||
}, | ||
"engineStrict": true, | ||
"keywords": [], | ||
"author": "Luciano Mammino and Mario Casciaro", | ||
"license": "MIT", | ||
"dependencies": {} | ||
} |
11 changes: 11 additions & 0 deletions
11
...nous-control-flow-patterns-with-promises-and-async-await/02-promisify/README.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
# 02-promisify | ||
|
||
This sample demonstrates how to *promisify* a Node.js-style callback-based function. | ||
|
||
## Run | ||
|
||
To run the example launch: | ||
|
||
```bash | ||
node index.js | ||
``` |
24 changes: 24 additions & 0 deletions
24
05-asynchronous-control-flow-patterns-with-promises-and-async-await/02-promisify/index.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
import { randomBytes } from 'node:crypto' | ||
|
||
function promisify(callbackBasedFn) { | ||
return function promisifiedFn(...args) { | ||
return new Promise((resolve, reject) => { | ||
const newArgs = [ | ||
...args, | ||
(err, result) => { | ||
if (err) { | ||
return reject(err) | ||
} | ||
|
||
resolve(result) | ||
}, | ||
] | ||
callbackBasedFn(...newArgs) | ||
}) | ||
} | ||
} | ||
|
||
const randomBytesP = promisify(randomBytes) | ||
randomBytesP(32).then(buffer => { | ||
console.log(`Random bytes: ${buffer.toString()}`) | ||
}) |
15 changes: 15 additions & 0 deletions
15
...synchronous-control-flow-patterns-with-promises-and-async-await/02-promisify/package.json
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
{ | ||
"name": "02-promisify", | ||
"version": "1.0.0", | ||
"description": "This sample demonstrates how to *promisify* a Node.js-style callback-based function", | ||
"type": "module", | ||
"scripts": {}, | ||
"engines": { | ||
"node": ">=22" | ||
}, | ||
"engineStrict": true, | ||
"keywords": [], | ||
"author": "Luciano Mammino and Mario Casciaro", | ||
"license": "MIT", | ||
"dependencies": {} | ||
} |
11 changes: 11 additions & 0 deletions
11
...flow-patterns-with-promises-and-async-await/03-wpromises-eb-spider-v2/README.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
# 03-wpromises-eb-spider-v2 | ||
|
||
Web spider example to demostrate sequential asynchronous execution with Promises | ||
|
||
## Run | ||
|
||
Install the necessary dependencies with `npm install` and then run: | ||
|
||
```bash | ||
node spider-cli.js https://loige.co | ||
``` |
19 changes: 19 additions & 0 deletions
19
...ontrol-flow-patterns-with-promises-and-async-await/03-wpromises-eb-spider-v2/package.json
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
{ | ||
"name": "03-wpromises-eb-spider-v2", | ||
"version": "1.0.0", | ||
"description": "Web spider example to demostrate sequential asynchronous execution with Promises", | ||
"type": "module", | ||
"scripts": {}, | ||
"engines": { | ||
"node": ">=22" | ||
}, | ||
"engineStrict": true, | ||
"keywords": [], | ||
"author": "Luciano Mammino and Mario Casciaro", | ||
"license": "MIT", | ||
"dependencies": { | ||
"htmlparser2": "^9.1.0", | ||
"mkdirp": "^3.0.1", | ||
"slug": "^9.1.0" | ||
} | ||
} |
83 changes: 83 additions & 0 deletions
83
...trol-flow-patterns-with-promises-and-async-await/03-wpromises-eb-spider-v2/pnpm-lock.yaml
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
11 changes: 11 additions & 0 deletions
11
...ntrol-flow-patterns-with-promises-and-async-await/03-wpromises-eb-spider-v2/spider-cli.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
import { spider } from './spider.js' | ||
|
||
const url = process.argv[2] | ||
const maxDepth = Number.parseInt(process.argv[3], 10) || 1 | ||
|
||
spider(url, maxDepth) | ||
.then(() => console.log('Downloaded complete')) | ||
.catch(err => { | ||
console.error(err) | ||
process.exit(1) | ||
}) |
61 changes: 61 additions & 0 deletions
61
...s-control-flow-patterns-with-promises-and-async-await/03-wpromises-eb-spider-v2/spider.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
import { readFile, writeFile } from 'node:fs/promises' | ||
import { dirname } from 'node:path' | ||
import { | ||
exists, | ||
get, | ||
getPageLinks, | ||
recursiveMkdir, | ||
urlToFilename, | ||
} from './utils.js' | ||
|
||
function saveFile(filename, content) { | ||
return recursiveMkdir(dirname(filename)) | ||
.then(() => writeFile(filename, content)) | ||
.then(() => content) | ||
} | ||
|
||
function download(url, filename) { | ||
console.log(`Downloading ${url} into ${filename}`) | ||
return get(url).then(content => saveFile(filename, content)) | ||
} | ||
|
||
function spiderLinks(currentUrl, body, maxDepth) { | ||
let promise = Promise.resolve() | ||
if (maxDepth === 0) { | ||
return promise | ||
} | ||
|
||
const links = getPageLinks(currentUrl, body) | ||
for (const link of links) { | ||
promise = promise.then(() => spider(link, maxDepth - 1)) | ||
} | ||
|
||
return promise | ||
} | ||
|
||
export function spider(url, maxDepth) { | ||
const filename = urlToFilename(url) | ||
|
||
return exists(filename).then(alreadyExists => { | ||
if (alreadyExists) { | ||
if (!filename.endsWith('.html')) { | ||
// ignoring non-HTML resources | ||
return | ||
} | ||
|
||
return readFile(filename, 'utf8').then(fileContent => | ||
spiderLinks(url, fileContent, maxDepth) | ||
) | ||
} | ||
|
||
// if file does not exist, download it | ||
return download(url, filename).then(fileContent => { | ||
// if the file is an HTML file, spider it | ||
if (filename.endsWith('.html')) { | ||
return spiderLinks(url, fileContent.toString('utf8'), maxDepth) | ||
} | ||
// otherwise, stop here | ||
return | ||
}) | ||
}) | ||
} |
78 changes: 78 additions & 0 deletions
78
...us-control-flow-patterns-with-promises-and-async-await/03-wpromises-eb-spider-v2/utils.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,78 @@ | ||
import { constants } from 'node:fs' | ||
import { access } from 'node:fs/promises' | ||
import { extname, join } from 'node:path' | ||
import { Parser } from 'htmlparser2' | ||
import { mkdirp } from 'mkdirp' | ||
import slug from 'slug' | ||
|
||
export function exists(filePath) { | ||
return access(filePath, constants.F_OK) | ||
.then(() => true) | ||
.catch(err => { | ||
if (err.code === 'ENOENT') { | ||
return false | ||
} | ||
|
||
throw err | ||
}) | ||
} | ||
|
||
export function urlToFilename(url) { | ||
const parsedUrl = new URL(url) | ||
const urlComponents = parsedUrl.pathname.split('/') | ||
const originalFileName = urlComponents.pop() | ||
const urlPath = urlComponents | ||
.filter(component => component !== '') | ||
.map(component => slug(component, { remove: null })) | ||
.join('/') | ||
const basePath = join(parsedUrl.hostname, urlPath) | ||
const missingExtension = !originalFileName || extname(originalFileName) === '' | ||
if (missingExtension) { | ||
return join(basePath, originalFileName, 'index.html') | ||
} | ||
|
||
return join(basePath, originalFileName) | ||
} | ||
|
||
// NOTE: this function is just for illustrative purposes. We are wrapping | ||
// fetch in a simplified API because at this point of the book we want | ||
// to demonstrate some promise based patterns | ||
export function get(url) { | ||
return fetch(url) | ||
.then(response => { | ||
if (!response.ok) { | ||
throw new Error(`Failed to fetch ${url}: ${response.statusText}`) | ||
} | ||
// NOTE: this loads all the content in memory and therefore is not suitable | ||
// to handle large payloads. | ||
// For large payloads, we would need to use a stream-based approach | ||
return response.arrayBuffer() | ||
}) | ||
.then(content => Buffer.from(content)) | ||
} | ||
|
||
// NOTE: this function is just for illustrative purposes. We are aliasing | ||
// `mkdirp` just to keep the same naming conventions as the ones we had in the callback-based | ||
// version of this example | ||
export const recursiveMkdir = mkdirp | ||
|
||
export function getPageLinks(currentUrl, body) { | ||
const url = new URL(currentUrl) | ||
const internalLinks = [] | ||
const parser = new Parser({ | ||
onopentag(name, attribs) { | ||
if (name === 'a' && attribs.href) { | ||
const newUrl = new URL(attribs.href, url) | ||
if ( | ||
newUrl.hostname === url.hostname && | ||
newUrl.pathname !== url.pathname | ||
) { | ||
internalLinks.push(newUrl.toString()) | ||
} | ||
} | ||
}, | ||
}) | ||
parser.end(body) | ||
|
||
return internalLinks | ||
} |