From bc9d8a025e906962ce484f9549fd6f91b0004356 Mon Sep 17 00:00:00 2001 From: Luciano Mammino Date: Sat, 5 Oct 2024 16:03:29 +0100 Subject: [PATCH] chore: working on chapter 4 --- .../01-web-spider/spider-cli.js | 5 +- .../README.md | 2 +- .../spider-cli.js | 5 +- .../spider.js | 2 +- .../utils.test.js | 16 - .../03-sequential-execution/README.md | 11 + .../03-sequential-execution/index.js | 26 + .../03-sequential-execution/package.json | 16 + .../04-web-spider-v2/README.md | 11 + .../04-web-spider-v2/fixtures/index.html | 3449 +++++++++++++++++ .../04-web-spider-v2/package.json | 19 + .../04-web-spider-v2/pnpm-lock.yaml | 83 + .../04-web-spider-v2/spider-cli.js | 13 + .../04-web-spider-v2/spider.js | 100 + .../04-web-spider-v2/utils.js | 86 + .../04-web-spider-v2/utils.test.js | 18 + .../05-callback-iteration-pattern/README.md | 11 + .../05-callback-iteration-pattern/index.js | 29 + .../package.json | 15 + .../06-web-spider-v3/README.md | 11 + .../06-web-spider-v3/package.json | 19 + .../06-web-spider-v3/pnpm-lock.yaml | 83 + .../06-web-spider-v3/spider-cli.js | 13 + .../06-web-spider-v3/spider.js | 102 + .../06-web-spider-v3/utils.js | 86 + .../README.md | 11 + .../index.js | 30 + .../package.json | 19 + 28 files changed, 4271 insertions(+), 20 deletions(-) delete mode 100644 04-asynchronous-control-flow-patterns-with-callbacks/02-web-spider-callback-discipline/utils.test.js create mode 100644 04-asynchronous-control-flow-patterns-with-callbacks/03-sequential-execution/README.md create mode 100644 04-asynchronous-control-flow-patterns-with-callbacks/03-sequential-execution/index.js create mode 100644 04-asynchronous-control-flow-patterns-with-callbacks/03-sequential-execution/package.json create mode 100644 04-asynchronous-control-flow-patterns-with-callbacks/04-web-spider-v2/README.md create mode 100644 04-asynchronous-control-flow-patterns-with-callbacks/04-web-spider-v2/fixtures/index.html create mode 100644 04-asynchronous-control-flow-patterns-with-callbacks/04-web-spider-v2/package.json create mode 100644 04-asynchronous-control-flow-patterns-with-callbacks/04-web-spider-v2/pnpm-lock.yaml create mode 100644 04-asynchronous-control-flow-patterns-with-callbacks/04-web-spider-v2/spider-cli.js create mode 100644 04-asynchronous-control-flow-patterns-with-callbacks/04-web-spider-v2/spider.js create mode 100644 04-asynchronous-control-flow-patterns-with-callbacks/04-web-spider-v2/utils.js create mode 100644 04-asynchronous-control-flow-patterns-with-callbacks/04-web-spider-v2/utils.test.js create mode 100644 04-asynchronous-control-flow-patterns-with-callbacks/05-callback-iteration-pattern/README.md create mode 100644 04-asynchronous-control-flow-patterns-with-callbacks/05-callback-iteration-pattern/index.js create mode 100644 04-asynchronous-control-flow-patterns-with-callbacks/05-callback-iteration-pattern/package.json create mode 100644 04-asynchronous-control-flow-patterns-with-callbacks/06-web-spider-v3/README.md create mode 100644 04-asynchronous-control-flow-patterns-with-callbacks/06-web-spider-v3/package.json create mode 100644 04-asynchronous-control-flow-patterns-with-callbacks/06-web-spider-v3/pnpm-lock.yaml create mode 100644 04-asynchronous-control-flow-patterns-with-callbacks/06-web-spider-v3/spider-cli.js create mode 100644 04-asynchronous-control-flow-patterns-with-callbacks/06-web-spider-v3/spider.js create mode 100644 04-asynchronous-control-flow-patterns-with-callbacks/06-web-spider-v3/utils.js create mode 100644 04-asynchronous-control-flow-patterns-with-callbacks/07-callback-unlimited-parallel-execution-pattern/README.md create mode 100644 04-asynchronous-control-flow-patterns-with-callbacks/07-callback-unlimited-parallel-execution-pattern/index.js create mode 100644 04-asynchronous-control-flow-patterns-with-callbacks/07-callback-unlimited-parallel-execution-pattern/package.json diff --git a/04-asynchronous-control-flow-patterns-with-callbacks/01-web-spider/spider-cli.js b/04-asynchronous-control-flow-patterns-with-callbacks/01-web-spider/spider-cli.js index edd0f6a..682964e 100644 --- a/04-asynchronous-control-flow-patterns-with-callbacks/01-web-spider/spider-cli.js +++ b/04-asynchronous-control-flow-patterns-with-callbacks/01-web-spider/spider-cli.js @@ -3,7 +3,10 @@ import { spider } from './spider.js' spider(process.argv[2], (err, filename, downloaded) => { if (err) { console.error(err) - } else if (downloaded) { + process.exit(1) + } + + if (downloaded) { console.log(`Completed the download of "${filename}"`) } else { console.log(`"${filename}" was already downloaded`) diff --git a/04-asynchronous-control-flow-patterns-with-callbacks/02-web-spider-callback-discipline/README.md b/04-asynchronous-control-flow-patterns-with-callbacks/02-web-spider-callback-discipline/README.md index d34cbf8..2010e3c 100644 --- a/04-asynchronous-control-flow-patterns-with-callbacks/02-web-spider-callback-discipline/README.md +++ b/04-asynchronous-control-flow-patterns-with-callbacks/02-web-spider-callback-discipline/README.md @@ -7,5 +7,5 @@ Refactored version of web spider that cleans up callback hell. Install the necessary dependencies with `npm install` and then run: ```bash -node spider-cli.js https://loige.co +node spider-cli.js https://loige.co 3 ``` diff --git a/04-asynchronous-control-flow-patterns-with-callbacks/02-web-spider-callback-discipline/spider-cli.js b/04-asynchronous-control-flow-patterns-with-callbacks/02-web-spider-callback-discipline/spider-cli.js index edd0f6a..682964e 100644 --- a/04-asynchronous-control-flow-patterns-with-callbacks/02-web-spider-callback-discipline/spider-cli.js +++ b/04-asynchronous-control-flow-patterns-with-callbacks/02-web-spider-callback-discipline/spider-cli.js @@ -3,7 +3,10 @@ import { spider } from './spider.js' spider(process.argv[2], (err, filename, downloaded) => { if (err) { console.error(err) - } else if (downloaded) { + process.exit(1) + } + + if (downloaded) { console.log(`Completed the download of "${filename}"`) } else { console.log(`"${filename}" was already downloaded`) diff --git a/04-asynchronous-control-flow-patterns-with-callbacks/02-web-spider-callback-discipline/spider.js b/04-asynchronous-control-flow-patterns-with-callbacks/02-web-spider-callback-discipline/spider.js index 376999e..2a07da3 100644 --- a/04-asynchronous-control-flow-patterns-with-callbacks/02-web-spider-callback-discipline/spider.js +++ b/04-asynchronous-control-flow-patterns-with-callbacks/02-web-spider-callback-discipline/spider.js @@ -21,7 +21,7 @@ function download(url, filename, cb) { if (err) { return cb(err) } - cb(null) + cb(null, content) }) }) } diff --git a/04-asynchronous-control-flow-patterns-with-callbacks/02-web-spider-callback-discipline/utils.test.js b/04-asynchronous-control-flow-patterns-with-callbacks/02-web-spider-callback-discipline/utils.test.js deleted file mode 100644 index 449ac0f..0000000 --- a/04-asynchronous-control-flow-patterns-with-callbacks/02-web-spider-callback-discipline/utils.test.js +++ /dev/null @@ -1,16 +0,0 @@ -import { deepEqual } from 'node:assert/strict' -import { test } from 'node:test' -import { urlToFilename } from './utils.js' - -test('urlToFilename', () => { - const cases = [ - ['https://example.com', 'example.com/index.html'], - ['https://example.com/test', 'example.com/test/index.html'], - ['https://example.com/test/', 'example.com/test/index.html'], - ['https://example.com/image.jpg', 'example.com/image.jpg'], - ['https://example.com/image.jpg?size=md', 'example.com/image.jpg'], - ] - for (const [url, expected] of cases) { - deepEqual(urlToFilename(url), expected) - } -}) diff --git a/04-asynchronous-control-flow-patterns-with-callbacks/03-sequential-execution/README.md b/04-asynchronous-control-flow-patterns-with-callbacks/03-sequential-execution/README.md new file mode 100644 index 0000000..3ab19ef --- /dev/null +++ b/04-asynchronous-control-flow-patterns-with-callbacks/03-sequential-execution/README.md @@ -0,0 +1,11 @@ +# 03-sequential-execution + +Simple example that demonstrates sequential tasks using callbacks + +## Run + +Run: + +```bash +node index.js +``` diff --git a/04-asynchronous-control-flow-patterns-with-callbacks/03-sequential-execution/index.js b/04-asynchronous-control-flow-patterns-with-callbacks/03-sequential-execution/index.js new file mode 100644 index 0000000..f954ee4 --- /dev/null +++ b/04-asynchronous-control-flow-patterns-with-callbacks/03-sequential-execution/index.js @@ -0,0 +1,26 @@ +function asyncOperation(cb) { + process.nextTick(cb) +} + +function task1(cb) { + asyncOperation(() => { + task2(cb) + }) +} + +function task2(cb) { + asyncOperation(() => { + task3(cb) + }) +} + +function task3(cb) { + asyncOperation(() => { + cb() // finally executes the callback + }) +} + +task1(() => { + // executed when task1, task2 and task3 are completed + console.log('tasks 1, 2 and 3 executed') +}) diff --git a/04-asynchronous-control-flow-patterns-with-callbacks/03-sequential-execution/package.json b/04-asynchronous-control-flow-patterns-with-callbacks/03-sequential-execution/package.json new file mode 100644 index 0000000..36f694d --- /dev/null +++ b/04-asynchronous-control-flow-patterns-with-callbacks/03-sequential-execution/package.json @@ -0,0 +1,16 @@ +{ + "name": "03-sequential-execution", + "version": "1.0.0", + "description": "Simple example that demonstrates sequential tasks using callbacks", + "engines": { + "node": ">=22" + }, + "engineStrict": true, + "type": "module", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "keywords": [], + "author": "", + "license": "ISC" +} diff --git a/04-asynchronous-control-flow-patterns-with-callbacks/04-web-spider-v2/README.md b/04-asynchronous-control-flow-patterns-with-callbacks/04-web-spider-v2/README.md new file mode 100644 index 0000000..08801a1 --- /dev/null +++ b/04-asynchronous-control-flow-patterns-with-callbacks/04-web-spider-v2/README.md @@ -0,0 +1,11 @@ +# 04-web-spider-v2 + +Web spider example to demonstrate sequential iteration + +## Run + +Install the necessary dependencies with `npm install` and then run: + +```bash +node spider-cli.js https://loige.co +``` diff --git a/04-asynchronous-control-flow-patterns-with-callbacks/04-web-spider-v2/fixtures/index.html b/04-asynchronous-control-flow-patterns-with-callbacks/04-web-spider-v2/fixtures/index.html new file mode 100644 index 0000000..a3f1fd4 --- /dev/null +++ b/04-asynchronous-control-flow-patterns-with-callbacks/04-web-spider-v2/fixtures/index.html @@ -0,0 +1,3449 @@ + + + + + Node.js Design Patterns Third Edition by Mario Casciaro and Luciano Mammino + + + + + + + +
+
+
+
+
+
+

Node.js Design Patterns: the ultimate guide to becoming a Node.js expert

+

The top-tier book for Node.js that will guide you from A to Z through the design and implementation of production-grade Node.js applications with tested patterns and techniques.

+ +
+
+ + Node.js Design Patterns book cover
+
+
+
+
+
+
+
+

The most practical and comprehensive Node.js book on the market

+
+
+
+

150 Working examples & 50 exercises

+

Put into practice everything you learn. Step-by-step explanation of code samples and engaging coding challenges at the end of every chapter.

+
+
+

13 Exquisitely crafted chapters

+

From the basics of the Node.js architecture to how to scale and distribute your application.

+
+
+

660 Pages packed with knowledge

+

In-depth explanations and examples, so that even if you are a novice you can follow and immediately apply advanced techniques.

+
+
+
+
+
+
+
+
+
+
+

Building modern extensible web applications is hard!

+
+
+
+
+ + Node.js Design Patterns book cover
+
+

If you are here, you have probably wandered a lot around the internet to find a valuable source to deepen your Node.js knowledge.

+

Maybe you have already used Node.js but you feel like you have not yet fully understood its key concepts.

+

You have read articles, and watched videos and webinars, but none of them have offered you a Node.js learning experience that seems complete.

+

What if we tell you that after a long search, you have finally landed on the right page?

+

You don't need to search for different sources and then put them together to obtain a detailed guide.

+

You need a comprehensive manual with TESTED content that guides you from A to Z in becoming a Node.js expert using both theoretical knowledge and practical exercises.

+ +
+
+
+
+
+
+
+
+
+

+ A Complete book that makes you confident with implementing scalable Node.js applications

+
+
+
+

Are you keen to understand how Node.js works under the hood?

+

A guide that teaches you all the best practices that you need to know to grow your career.

+

You will finally understand how JavaScript and Node.js work and how to build performant and scalabale web applications.

+

You will find clear writing, diagrams, and real-world code examples, taking a deep dive into projects using technologies like Redis or Fastify.

+

All this will be possible thanks to Node.js Design Patterns: the first book that covers all the Node.js topics that you need to become a professional.

+

From theory to practice, from real-world exercises to useful best practices.

+

Start your journey to become a Node.js pro now!

+ +
+
+ + Node.js Design Patterns book cover
+
+
+
+
+
+
+
+
+

+ More than just Design Patterns: a book with Everything you need to know about Node.js

+

13 chapters carefully crafted to explore and master various aspects of Node.js professional development

+
+
+
    +
  • Explore the basics of Node.js, analyzing its asynchronous event-driven architectures and design patterns (including event emitters, callbacks, promises and async/await);
  • +
  • Dive into Node.js streams and some of the most famous Gang of Four design patterns reinterpreted in the context of Node.js plus some novel patterns that are unique to JavaScript and Node.js;
  • +
  • Explore advanced topics such as Universal JavaScript with Node.js, React and Webpack;
  • +
  • Discover best practices to scale Node.js services, microservices and messaging patterns for enterprise-grade distributed applications;
  • +
+
+
+
+
+
+

+ What you will find in the book:

+
+
+
    +
  1. +

    The Node.js Platform

    +

    Learn about the Node.js philosophy, the reactor patterns and the differences between JavaScript on the browser and Node.js on the server.

    +
  2. +
  3. +

    The Module System

    +

    Learn how to leverage the powerful Node.js module system and discover the main differences between CommonJS and ESM.

    +
  4. +
  5. +

    Callbacks and Events

    +

    Discover the callback pattern, how it works and the conventions used in Node.js. Learn how to avoid pitfalls and when to take advantage of the observer pattern using Node.js built-in event emitter.

    +
  6. +
  7. +

    Asynchronous Control Flow Patterns with Callbacks

    +

    Lean how to avoid callback hell and explore common asynchronous patterns such as sequential execution, sequential iteration, parallel execution and limited parallel execution.

    +
  8. +
  9. +

    Asynchronous Control Flow Patterns with Promises and Async/Await

    +

    Find out how promises work and how to use them effectively to implement various asynchronous control flow patterns. Explore the modern async/await syntax, the main tool today for dealing with asynchronous code in Node.js

    +
  10. +
  11. +

    Coding with Streams

    +

    Understand why streams are so important in Node.js. Learn how to use standard streams and how to create custom ones. Explore various streaming patterns and learn how to build powerful streaming pipelines.

    +
  12. +
  13. +

    Creational Design Patterns

    +

    Learn about the most famous creational design patterns in Node.js: the Factory pattern, the Revealing Constructor pattern, the Builder pattern. Finally, explore the Singleton pattern and the Dependency Injection pattern.

    +
  14. +
  15. +

    Structural Design Patterns

    +

    Discover how to implement and use the Proxy, the Decorator and the Adapter pattern in Node.js.

    +
  16. +
  17. +

    Behavioural Design Patterns

    +

    Learn how to implement and leverage some of the most well known behavioural design patterns in the context of Node.js: the Strategy pattern, the State pattern, the Template pattern, the Iterator pattern, the Middleware pattern, and the Command pattern.

    +

    + + Free Chapter! +

    +
  18. +
  19. +

    Universal JavaScript

    +

    Explore the fundamentals of JavaScript cross-platform development and learn how to share code between the browser and Node.js. Learn how to leverage React.js to build a complete universal JavaScript application.

    +
  20. +
  21. +

    Advanced Recipes

    +

    Discover well-known recipes to deal with some more advanced Node.js intricacies such as dealing with asynchronously initialized components, performing asynchronous request batching and caching, canceling asynchronous operations and running CPU-bound tasks.

    +
  22. +
  23. +

    Scalability and Architectural Patterns

    +

    Master the art of Node.js scalability by learning about the "Scale Cube", discover how to run multiple instances of the same application and how to use load balancers and service registers. Learn how to use containers and containers orchestration platforms such as Kubernetes. Finally, find out how to design and build microservices architectures.

    +
  24. +
  25. +

    Messaging and Integration Patterns

    +

    Learn how to integrate complex distributed Node.js applications using the most popular messaging systems. Learn how to implement the most common messaging patterns on top of ZeroMQ, RabbitMQ and Redis Streams.

    +
  26. +
+
+
+
+ +
+
+
+
+
+
+

+ What are the Benefits of choosing Node.js Design Patterns?

+
+
+
+

Get a 360° knowledge of the Node.js ecosystem

+

660 pages packed with deep knowledge of Node.js from a theoretical and practical point of view, to become a Node.js pro and apply what you discover to real-world projects.

+
+
+

Thoroughly test your understanding

+

You will find practical exercises at the end of each chapter to test what you have previously learnt with the theory.

+
+
+

Join a thriving Node.js dev community

+

Gain access to an active community on GitHub, where other developers who are reading the book share ideas, comments, and correct exercises together.

+
+
+
+ +
+
+
+
+
+

+ Meet the authors

+
+
+

Meet Mario and Luciano, two passionate software engineers with a shared love for Node.js and more than 30 years of collective experience on the field!

+
+
+
+
+
+
+
+

Mario Casciaro

+
+
+
+
+ + Mario Casciaro's picture
+
+

Mario is currently the CTO of D4H Technologies, where he creates software for emergency management. In the past, Mario has worked at IBM as a team lead and also founded a couple of software companies.

+ +
+
+
+
+
+
+
+

Luciano Mammino

+
+
+
+
+ + Luciano Mammino's picture
+
+

Luciano is a Senior Architect at fourTheorem where he is helping companies to get the best out of the Cloud and AWS. He is an active speaker and in the last few years he has delivered more than 130 talks in conferences and meetups around the World.

+ +
+
+
+
+
+
+
+
+
+
+

+ What the experts say

+
+
+
+
+

+ + Joe Karlsson's profile picture

+
+
+

+ Joe Karlsson + — Developer Advocate at Tinybird +

+

I've read basically every Node.js book ever published, and this is my personal favorite (and best) by far. Even months after reading all the way through this text, I still learn new things each time I browse through it. What can I say, this is a must for anyone writing code in Node. js. Everything in the book, including the code examples, are very useful in practice.

+
+
+
+
+
+
+

+ + Gleb Bahmutov's profile picture

+
+
+

+ Gleb Bahmutov + — Senior Director Of Engineering at Mercari +

+

Wow! This book ... is amazing. After many years of programming with JavaScript, I still have learnt so much from this book. It covers many topics relevant to large Node.js applications with ease, with many relevant code snippets making the material easily approachable. While not for beginners, it is a great book for anyone looking to take their JS applications to the higher level.

+
+
+
+
+
+
+

+ + Radoslav Stankov's profile picture

+
+
+

+ Radoslav Stankov + — CTO and Co-Founder at Angry Building +

+

Node.js Design Patterns is an excellent resource for learning. I learned a lot of things that I thought I already knew. My favorite part is the Commonjs and ES6 import systems and a lot of the more advanced recipes.

+
+
+
+
+
+
+

+ + Ire Aderinokun's profile picture

+
+
+

+ Ire Aderinokun + — Frontend Engineer, Entrepreneur, Investor +

+

Node.js Design Patterns is a really in-depth look into the world of Node.js. Everything you ever wanted to know, and more! It’s very comprehensive, and gets into the details of how Node.js works. It’s a perfect book for anyone who already knows a bit about Node.js, and is looking to deepen their knowledge.

+
+
+
+
+
+
+

+ + Ersel Aker's profile picture

+
+
+

+ Ersel Aker + — Co-Founder at futurecast.studio +

+

Read through a few chapters of Node.js Design Patterns this morning. It's an excellent reference book covering everything from from the intricacies of the Node.js runtime to scaling your services to serve millions of users. Grabbed another copy for our engineering team to use as a study tool.

+
+
+
+
+
+
+

+ + Mike Rourke's profile picture

+
+
+

+ Mike Rourke + — Software Engineer at a.i. solutions and Author +

+

There is a lot of useful content in this book, and I think the title doesn't do it justice. If you want a comprehensive book about Node.js that not only acts as a reference for best practices and design patterns, but also as a one-stop shop for understanding how Node.js works behind the scenes, look no further!

+
+
+
+
+
+
+

+ + Maya Shavin's profile picture

+
+
+

+ Maya Shavin + — Senior Software Engineer at Microsoft and Author +

+

This book surprised me for how detailed it is! It contains a lot of useful and easy-to-understand information. For someone familiar with the Node.js, I'm intrigued by how much depth the book provides in working with Node.js. Every topic is approached in a very straightforward way: while being practical, this book offers different readers' levels - from the most juniors to the experienced ones - an excellent reference to apply design patterns in any real scale Node.js application. Totally going to bookmark it as my favorite Node.js book!

+
+
+
+
+
+
+

+ + Kostas Bariotis's profile picture

+
+
+

+ Kostas Bariotis + — Sr. Software Engineer at Mixmax +

+

Node.js Design Patterns is an amazing resource for every level of Node.js developers. It covers important design patters that are used in the Node.js world (modules, callbacks, asynchronicity, etc) and explores critical bits of the core library (promises, streams, event emitters, etc). Finally, it provides best practices on running Node.js as part of a bigger architecture where you need to horizontally scale an application, balance the incoming load and setup a message bus in between your services.

+
+
+
+
+
+
+

+ + David Wells's profile picture

+
+
+

+ David Wells + — Full Stack Engineer at Vendia +

+

If you want a deep dive into how Node.js works and the available design patterns, I'd recommend checking out Mario and Luciano's new book. I particularly enjoyed the section on using streams. After reading, I realized haven't been leveraging streams enough in my projects.

+
+
+
+
+
+
+

+ + Simon Høiberg's profile picture

+
+
+

+ Simon Høiberg + — Software Developer and Startup CEO +

+

Node.js Design Patterns is a great, in-depth resource for both beginners and advanced. Even though I've been writing Node.js applications for the past 5 years, I learned a lot from this comprehensive piece of work. By far the best Node.js book currently out there.

+
+
+
+
+
+
+

+ + Mike Alche's profile picture

+
+
+

+ Mike Alche + — React, React Native & Node.js software consultant +

+

Every time a new Node.js book comes out I read it. I think there isn’t a single one that I haven’t read. And out of all of them I must say that Node.js Design Patterns is by far — and I mean it BY FAR— the undisputed best. To put it in the most honest way possible: this is the book I go to read when preparing to teach a Node.js class to groups of software engineers.

+
+
+
+
+
+
+

+ + Theodore Vorillas's profile picture

+
+
+

+ Theodore Vorillas + — Independent Software Engineer and JavaScript expert +

+

Node.js Design Patterns is a must read, an excellent resource for learning how to build Node.js applications. I loved the fact that you can actually learn new stuff regardless your experience. My favorite chapters are about scaling a production application (Universal JavaScript, Scalability and Architectural Patterns, Advanced Recipes)... and the coding samples are also available for free on GitHub!

+
+
+
+
+
+
+

+ + David Gonzalez's profile picture

+
+
+

+ David Gonzalez + — Principal Engineer at Cloudsmith and author +

+

I am a hardcore techie, big into Node.js and I have to say I have learnt a lot by just going through Node.js Design Patterns. The level of detail and quality of the examples are incredible and in general, it is a good book have as a reference to solve any problem that might arise while code (not only in Node.js). This is a book I recommend to my students to go from 0 to hero and keep it as a reference forever. The previous edition was good, but this one is just out of this world.

+
+
+
+
+
+
+

+ + Maxim Salnikov's profile picture

+
+
+

+ Maxim Salnikov + — Developer Productivity Business Lead at Microsoft +

+

I have "Node.js Design Patterns" always at hand both when I start quick new projects and when the time comes to refactor the larger and mature ones. It's an invaluable and well-structured source of patterns, best practices, and guidelines. Also, it’s always good to know that particular features are in use in well-known projects - thanks to the “In the wild” section. As a full-stack developer focused on the front-end, I especially enjoyed a chapter about universal JavaScript.

+
+
+
+
+ +
+
+
+
+
+
+
+

+ Who is Node.js Design Patterns for?

+
+
+
    +
  • For those who already know the basics of the JavaScript language and want to become a Node.js professional +
  • +
  • For those who want to understand how the event loop works and learn to use Node.js to its full potential without errors and in the most efficient way +
  • +
  • For those who want to learn how to adapt classic design patterns to Node.js and discover Node.js specific design patterns +
  • +
  • For those who want to learn how to use production ready tools such as LevelDB, Redis, RabbitMQ, ZeroMQ in the Node.js context to develop real applications that can scale to millions of users +
  • +
+
+
+
+
+
+

+ Why shouldn't you Miss this book?

+
+
+
+
+

+ Much more than just a textbook! +

+

+ Node.js Design Patterns is a specific manual covering Node.js topics from A to Z.

+

You will have the opportunity of applying what you are learning with lots of exercises.

+

It is much more practical than a regular manual, just to make sure you understand actual market needs.

+

You will also have the possibility of connecting with an entire developers community on GitHub and the authors.

+
+
+
+
+ + Node.js Design Patterns. open book showing some of the diagrams about the reactor pattern
+
+
+
+

+ Are you ready to take your Node.js knowledge to the next level?

+
+ +
+
+
+
+
+
+
+ + Mario Casciaro's picture
+
+
+

+ Developers' Favorite book to learn Node.js like a Pro

+
+
+

+ Node.js Design Patterns is the first book on the market that can help you become a Node.js professional not just with theory, but also with practical exercises, best practices and design patterns.

+
+
+ +
+
+
+
+
+
+
+
+
+

+ Rated 4.7 with 250+ reviews on Amazon

+
+
+
+

+ Extremely helpful +

+
+ + + + + +
+

From the title, the book sounded like it would be exactly what I needed and I am happy to say it is. There are always several ways of doing things and the pitfalls/best practices (and reasons behind them) are not always immediately obvious. This book gives you the tools to reason about which pattern is going to be best for a certain task, which is so helpful. I really appreciate the quality of the resource. They also provide exercises at the end of the chapter which I think is super helpful for reinforcing concepts and keeping things interactive.

+

+ Rebecca + +

+
+
+

+ Highly recommend to add this to your toolkit if you're developing Node.js applications +

+
+ + + + + +
+

I've been developing for Node.js for a number of years, and this book helps me to understand concepts and things under the hood I’ve never had a chance to explore, all in a single source. I’m amazed that design patterns are covered with JS codes, as it shows me how to design and architect Node applications better to run at scale. Another thing I love is the hands-on aspects in guiding me to set up and run production-grade services such as task distribution, callbacks, concurrency and streams. Highly recommend to add this to your toolkit if you're developing Node.js applications.

+

+ James Ma + +

+
+
+

+ Clean and well structured +

+
+ + + + + +
+

This book gets straight to the point, and there are many practical examples. You can find fundamentals and more advanced notions. Each chapter is well designed and the presentation of topics follows a well-structured logic.

+

+ Valerio + +

+
+
+

+ Deep knowledge about Node.js +

+
+ + + + + +
+

I found this book very useful and knowledgeable. Great and clean up to date content. You can either read it in order or use it as a reference. Love it. Thank you

+

+ Or Hasson + +

+
+
+

+ Excelent content +

+
+ + + + + +
+

I am liking the book, it explains a lot of low level concepts of Node.js and the part of design patterns shows great exemples how to use the pattern explained, instead of just theory. Worth the purchase. I really recommend.

+

+ Renan Truppel Ayoub + +

+
+
+

+ Wonderful book, the best for Node.js. A book to have in your library +

+
+ + + + + +
+

A really well structured book, with clear and precise examples, aimed at explaining the concepts set out. Very complete and comprehensive, it covers practically all aspects and approaches of Node. Great Job for Mario and Luciano.

+

+ Massimiliano C. + +

+
+
+

+ Indispensable +

+
+ + + + + +
+

A book that I always recommend to junior developers or those who want to improve their skills with Node. Well structured and with the necessary content to understand how to work professionally with this language.

+

+ Juan + +

+
+
+

+ Deep dive into Node.js, JavaScript style +

+
+ + + + + +
+

An excellent book that covers many topics, including design patterns, with every example in JavaScript. The author does a great job of explaining each subject with multiple examples refactoring each into the optimal solution.

+

+ NeoModulus + +

+
+
+
+ + Node.js Design Patterns reviews from Amazon.com + +

+ Read all the reviews +

+
+
+
+
+
+
+
+
+

+ FAQs

+
+
+ +
+
+
+

+ +

+

This book is for developers and software architects who have some prior knowledge of JavaScript and Node.js and now want to get the most out of these technologies in terms of productivity, design quality, and scalability. Software professionals with intermediate experience in Node.js and JavaScript will also find valuable the more advanced patterns and techniques presented in this book.

+
+
+
+ +
+
+
+

+ +

+

This book assumes that you have an intermediate understanding of JavaScript, web application development, databases, and software design principles. If you are new to JavaScript but you are familiar with other languages and technologies this book can still be a valuable resource for you but we do recommend you to get familiar with the basics of JavaScript first. Some good FREE resources you could check out are Eloquent JavaScript, JavaScript introduction by W3C and JavaScript in 30 days.

+
+
+
+ +
+
+
+

+ +

+

This book will get you comfortable with writing asynchronous code by leveraging callbacks, promises, and the async/await syntax. It will teach you how to leverage Node.js streams to create data-driven asynchronous processing pipelines, how to implement well-known software design patterns to create production grade applications, and how to share code between Node.js and the browser, taking full advantage of full-stack JavaScript. Finally, this book will show you how to build and scale microservices and distributed systems powered by Node.js and how to use Node.js in conjunction with other powerful technologies such as Redis, RabbitMQ, ZeroMQ, and LevelDB.

+
+
+
+ +
+
+
+

+ +

+

+ No. Node.js Design Patterns is the result of countless hours of hard work by the authors, the reviewers, and many professionals at Packt, so we simply can't afford to give it away for free. If you want to have a feeling about the quality of the content in the book, you can download Chapter 9: Behavioural Design Patterns for FREE. You can also check out for FREE all the code examples available in the book on the official GitHub repository.

+
+
+
+ +
+
+
+

+ +

+

The book has 13 chapters totaling about 660 pages.

+
+
+
+ +
+
+
+

+ +

+

The book was written to give you continuity throughout the chapters so that you can enjoy reading the book from the first to the very last page. Although, if you are already familiar with many Node.js concepts and design patterns, you can easily skim through the chapters and focus only on the content that matters the most to you. Check out the Table of Contents if you want to have a better feeling on how the book is structured.

+
+
+
+ +
+
+
+

+ +

+

The central part of the book covers the most traditional design patterns from the gang of four book, but it also covers many other design patterns that are specific to JavaScript and Node.js. This is the full list of patterns covered in the book: Factory, Builder, Revealing Constructor, Singleton, Dependency Injection, Proxy, Decorator, Adapter, Strategy, State, Template, Iterator (including generators and async iterators), Middleware, and Command.

+
+
+
+ +
+
+
+

+ +

+

The third edition of this book (released in July 2020) has been updated to cover the features of the most recent Node.js LTS version (Node.js 14). Node.js Design Patterns (third edition) leverages modern best practices and Node.js features such as Async/Await and EcmaScript Modules (ESM).

+
+
+
+ +
+
+
+

+ +

+

All the code examples contained in the book are available on GitHub and can be accessed for FREE.

+
+
+
+ +
+
+
+

+ +

+

Node.js Design Patterns provides a mix of simple examples and real-life applications to gradually introduce you to new topics and patterns. Some examples of real-life applications are:

+
    +
  • Your own module loader
  • +
  • A website spider and downloader
  • +
  • A streaming file archive pipeline
  • +
  • A plugin for LevelUp
  • +
  • A full stack universal JavaScript website with Fastify, React and Webpack
  • +
  • A dynamic HTTP load balancer using Consul
  • +
  • A peer-to-peer network for web services
  • +
  • A distributed real-time chat application with an interactive web UI which leverages ZeroMQ, RabbitMQ and Redis Streams
  • +
  • A worker pool for background job executions using ZeroMQ, RabbitMQ and Redis Streams
  • +
+

You can checkout the source code for these examples for FREE on GitHub.

+
+
+
+ +
+
+
+

+ +

+

Every chapter comes with a set of engaging coding challenges and exercises crafted to put your new learnings into practice. Node.js Design Patterns contains 50 exercises in total.

+
+
+
+ +
+
+
+

+ +

+
+
+
+
+
+
+
+
+
+
+

Still in doubt?

+

We know there's tons of material about Node.js on the internet. And some of the available content is actually good.

+

We just want to make sure you know that Node.js Design Patterns is the most complete book about Node.js available right now to help you become a professional.

+

Maybe you are not sure if the content will be clear and useful enough for your needs?

+

This is why we want to give you a gift… A free chapter from “Node.js Design patterns” to help you make an even more informed purchase decision.

+

+ 54 pages to learn how to implement and leverage some of the most well known behavioural design patterns in the context of Node.js: Strategy, State, Template, Iterator, Middleware, and Command Pattern.

+
+ + +
+
+
    +
    +
    + +
    +
    + +
    +
    +
    +
    + + Node.js Design Patterns chapter 9 behavioral design patterns
    +
    +
    +
    +
    + + + + \ No newline at end of file diff --git a/04-asynchronous-control-flow-patterns-with-callbacks/04-web-spider-v2/package.json b/04-asynchronous-control-flow-patterns-with-callbacks/04-web-spider-v2/package.json new file mode 100644 index 0000000..c4d6d0d --- /dev/null +++ b/04-asynchronous-control-flow-patterns-with-callbacks/04-web-spider-v2/package.json @@ -0,0 +1,19 @@ +{ + "name": "04-web-spider-v2", + "version": "1.0.0", + "description": "Web spider example to demonstrate sequential iteration", + "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" + } +} diff --git a/04-asynchronous-control-flow-patterns-with-callbacks/04-web-spider-v2/pnpm-lock.yaml b/04-asynchronous-control-flow-patterns-with-callbacks/04-web-spider-v2/pnpm-lock.yaml new file mode 100644 index 0000000..cf42260 --- /dev/null +++ b/04-asynchronous-control-flow-patterns-with-callbacks/04-web-spider-v2/pnpm-lock.yaml @@ -0,0 +1,83 @@ +lockfileVersion: '9.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +importers: + + .: + dependencies: + htmlparser2: + specifier: ^9.1.0 + version: 9.1.0 + mkdirp: + specifier: ^3.0.1 + version: 3.0.1 + slug: + specifier: ^9.1.0 + version: 9.1.0 + +packages: + + dom-serializer@2.0.0: + resolution: {integrity: sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==} + + domelementtype@2.3.0: + resolution: {integrity: sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==} + + domhandler@5.0.3: + resolution: {integrity: sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==} + engines: {node: '>= 4'} + + domutils@3.1.0: + resolution: {integrity: sha512-H78uMmQtI2AhgDJjWeQmHwJJ2bLPD3GMmO7Zja/ZZh84wkm+4ut+IUnUdRa8uCGX88DiVx1j6FRe1XfxEgjEZA==} + + entities@4.5.0: + resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==} + engines: {node: '>=0.12'} + + htmlparser2@9.1.0: + resolution: {integrity: sha512-5zfg6mHUoaer/97TxnGpxmbR7zJtPwIYFMZ/H5ucTlPZhKvtum05yiPK3Mgai3a0DyVxv7qYqoweaEd2nrYQzQ==} + + mkdirp@3.0.1: + resolution: {integrity: sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg==} + engines: {node: '>=10'} + hasBin: true + + slug@9.1.0: + resolution: {integrity: sha512-ioOsCfzQSu+D6NZ8XMCR8IW9FgvF8W7Xzz56hBkB/ALvNaWeBs2MUvvPugq3GCrxfHPFeK6hAxGkY/WLnfX2Lg==} + hasBin: true + +snapshots: + + dom-serializer@2.0.0: + dependencies: + domelementtype: 2.3.0 + domhandler: 5.0.3 + entities: 4.5.0 + + domelementtype@2.3.0: {} + + domhandler@5.0.3: + dependencies: + domelementtype: 2.3.0 + + domutils@3.1.0: + dependencies: + dom-serializer: 2.0.0 + domelementtype: 2.3.0 + domhandler: 5.0.3 + + entities@4.5.0: {} + + htmlparser2@9.1.0: + dependencies: + domelementtype: 2.3.0 + domhandler: 5.0.3 + domutils: 3.1.0 + entities: 4.5.0 + + mkdirp@3.0.1: {} + + slug@9.1.0: {} diff --git a/04-asynchronous-control-flow-patterns-with-callbacks/04-web-spider-v2/spider-cli.js b/04-asynchronous-control-flow-patterns-with-callbacks/04-web-spider-v2/spider-cli.js new file mode 100644 index 0000000..abe2be6 --- /dev/null +++ b/04-asynchronous-control-flow-patterns-with-callbacks/04-web-spider-v2/spider-cli.js @@ -0,0 +1,13 @@ +import { spider } from './spider.js' + +const url = process.argv[2] +const maxDepth = Number.parseInt(process.argv[3], 10) || 1 + +spider(url, maxDepth, err => { + if (err) { + console.error(err) + process.exit(1) + } + + console.log('Downloaded complete') +}) diff --git a/04-asynchronous-control-flow-patterns-with-callbacks/04-web-spider-v2/spider.js b/04-asynchronous-control-flow-patterns-with-callbacks/04-web-spider-v2/spider.js new file mode 100644 index 0000000..21934ed --- /dev/null +++ b/04-asynchronous-control-flow-patterns-with-callbacks/04-web-spider-v2/spider.js @@ -0,0 +1,100 @@ +import { readFile, writeFile } from 'node:fs' +import { dirname } from 'node:path' +import { + exists, + get, + getPageLinks, + recursiveMkdir, + urlToFilename, +} from './utils.js' + +function saveFile(filename, content, cb) { + recursiveMkdir(dirname(filename), err => { + if (err) { + return cb(err) + } + writeFile(filename, content, cb) + }) +} + +function download(url, filename, cb) { + console.log(`Downloading ${url} into ${filename}`) + get(url, (err, content) => { + if (err) { + return cb(err) + } + saveFile(filename, content, err => { + if (err) { + return cb(err) + } + cb(null, content) + }) + }) +} + +function spiderLinks(currentUrl, body, maxDepth, cb) { + if (maxDepth === 0) { + // Remember Zalgo from Chapter 3? + return process.nextTick(cb) + } + + const links = getPageLinks(currentUrl, body) + if (links.length === 0) { + return process.nextTick(cb) + } + + function iterate(index) { + if (index === links.length) { + return cb() + } + + spider(links[index], maxDepth - 1, err => { + if (err) { + return cb(err) + } + iterate(index + 1) + }) + } + + iterate(0) +} + +export function spider(url, maxDepth, cb) { + const filename = urlToFilename(url) + + exists(filename, (err, alreadyExists) => { + if (err) { + // error checking the file + return cb(err) + } + + if (alreadyExists) { + if (!filename.endsWith('.html')) { + // ignoring non-HTML resources + return cb() + } + + readFile(filename, 'utf8', (err, fileContent) => { + if (err) { + // error reading the file + return cb(err) + } + return spiderLinks(url, fileContent, maxDepth, cb) + }) + } else { + // The file does not exist, download it + download(url, filename, (err, fileContent) => { + if (err) { + // error downloading the file + return cb(err) + } + // if the file is an HTML file, spider it + if (filename.endsWith('.html')) { + return spiderLinks(url, fileContent.toString('utf8'), maxDepth, cb) + } + // otherwise, stop here + return cb() + }) + } + }) +} diff --git a/04-asynchronous-control-flow-patterns-with-callbacks/04-web-spider-v2/utils.js b/04-asynchronous-control-flow-patterns-with-callbacks/04-web-spider-v2/utils.js new file mode 100644 index 0000000..be35ad1 --- /dev/null +++ b/04-asynchronous-control-flow-patterns-with-callbacks/04-web-spider-v2/utils.js @@ -0,0 +1,86 @@ +import { constants, access } from 'node:fs' +import { extname, join } from 'node:path' +import { Parser } from 'htmlparser2' +import { mkdirp } from 'mkdirp' +import slug from 'slug' + +export function exists(filePath, cb) { + access(filePath, constants.F_OK, err => { + if (err) { + if (err.code === 'ENOENT') { + // the file does not exist + return cb(null, false) + } + // unexpected error checking the file + return cb(err) + } + + // the file exists + return cb(null, true) + }) +} + +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 callback-based API because at this point of the book we want +// to demonstrate callback based patterns +export function get(url, cb) { + 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 => cb(null, Buffer.from(content))) + .catch(err => cb(err)) +} + +// NOTE: this function is just for illustrative purposes. We are wrapping +// mkdirp in a callback-based API because at this point of the book we want +// to demonstrate callback based patterns +export function recursiveMkdir(path, cb) { + mkdirp(path) + .then(() => cb(null)) + .catch(e => cb(e)) +} + +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 +} diff --git a/04-asynchronous-control-flow-patterns-with-callbacks/04-web-spider-v2/utils.test.js b/04-asynchronous-control-flow-patterns-with-callbacks/04-web-spider-v2/utils.test.js new file mode 100644 index 0000000..312f03f --- /dev/null +++ b/04-asynchronous-control-flow-patterns-with-callbacks/04-web-spider-v2/utils.test.js @@ -0,0 +1,18 @@ +import { deepEqual } from 'node:assert/strict' +import { readFile } from 'node:fs/promises' +import { join } from 'node:path' +import { test } from 'node:test' +import { getPageLinks } from './utils.js' + +test('getPageLinks', async () => { + const sampleFile = join(import.meta.dirname, 'fixtures', 'index.html') + const rawHtml = await readFile(sampleFile, 'utf-8') + const links = getPageLinks('https://www.nodejsdesignpatterns.com', rawHtml) + deepEqual(links, [ + 'https://www.nodejsdesignpatterns.com/blog/node-js-stream-consumer/', + 'https://www.nodejsdesignpatterns.com/blog/javascript-async-iterators/', + 'https://www.nodejsdesignpatterns.com/blog/node-js-development-with-docker-and-docker-compose/', + 'https://www.nodejsdesignpatterns.com/blog/node-js-race-conditions/', + 'https://www.nodejsdesignpatterns.com/blog/5-ways-to-install-node-js/', + ]) +}) diff --git a/04-asynchronous-control-flow-patterns-with-callbacks/05-callback-iteration-pattern/README.md b/04-asynchronous-control-flow-patterns-with-callbacks/05-callback-iteration-pattern/README.md new file mode 100644 index 0000000..c6e9170 --- /dev/null +++ b/04-asynchronous-control-flow-patterns-with-callbacks/05-callback-iteration-pattern/README.md @@ -0,0 +1,11 @@ +# 05-callback-iterator-pattern + +Simple example that demonstrates the callback iteration pattern + +## Run + +Run: + +```bash +node index.js +``` diff --git a/04-asynchronous-control-flow-patterns-with-callbacks/05-callback-iteration-pattern/index.js b/04-asynchronous-control-flow-patterns-with-callbacks/05-callback-iteration-pattern/index.js new file mode 100644 index 0000000..d7de206 --- /dev/null +++ b/04-asynchronous-control-flow-patterns-with-callbacks/05-callback-iteration-pattern/index.js @@ -0,0 +1,29 @@ +const tasks = [ + cb => { + console.log('Task 1') + setTimeout(cb, 1000) + }, + cb => { + console.log('Task 2') + setTimeout(cb, 1000) + }, + cb => { + console.log('Task 3') + setTimeout(cb, 1000) + }, +] + +function iterate(index) { + if (index === tasks.length) { + return finish() + } + const task = tasks[index] + task(() => iterate(index + 1)) +} + +function finish() { + // iteration completed + console.log('All tasks executed') +} + +iterate(0) diff --git a/04-asynchronous-control-flow-patterns-with-callbacks/05-callback-iteration-pattern/package.json b/04-asynchronous-control-flow-patterns-with-callbacks/05-callback-iteration-pattern/package.json new file mode 100644 index 0000000..45ddb1d --- /dev/null +++ b/04-asynchronous-control-flow-patterns-with-callbacks/05-callback-iteration-pattern/package.json @@ -0,0 +1,15 @@ +{ + "name": "05-callback-iteration-pattern", + "version": "1.0.0", + "description": "Simple example that demonstrates the callback iteration pattern", + "type": "module", + "scripts": {}, + "engines": { + "node": ">=22" + }, + "engineStrict": true, + "keywords": [], + "author": "Luciano Mammino and Mario Casciaro", + "license": "MIT", + "dependencies": {} +} diff --git a/04-asynchronous-control-flow-patterns-with-callbacks/06-web-spider-v3/README.md b/04-asynchronous-control-flow-patterns-with-callbacks/06-web-spider-v3/README.md new file mode 100644 index 0000000..2d509db --- /dev/null +++ b/04-asynchronous-control-flow-patterns-with-callbacks/06-web-spider-v3/README.md @@ -0,0 +1,11 @@ +# 06-web-spider-v3 + +Web spider example to demonstrate unlimited parallel execution. + +## Run + +Install the necessary dependencies with `npm install` and then run: + +```bash +node spider-cli.js https://loige.co 3 +``` diff --git a/04-asynchronous-control-flow-patterns-with-callbacks/06-web-spider-v3/package.json b/04-asynchronous-control-flow-patterns-with-callbacks/06-web-spider-v3/package.json new file mode 100644 index 0000000..c5aaac5 --- /dev/null +++ b/04-asynchronous-control-flow-patterns-with-callbacks/06-web-spider-v3/package.json @@ -0,0 +1,19 @@ +{ + "name": "06-web-spider-v3", + "version": "1.0.0", + "description": "Web spider example to demonstrate unlimited parallel execution", + "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" + } +} diff --git a/04-asynchronous-control-flow-patterns-with-callbacks/06-web-spider-v3/pnpm-lock.yaml b/04-asynchronous-control-flow-patterns-with-callbacks/06-web-spider-v3/pnpm-lock.yaml new file mode 100644 index 0000000..cf42260 --- /dev/null +++ b/04-asynchronous-control-flow-patterns-with-callbacks/06-web-spider-v3/pnpm-lock.yaml @@ -0,0 +1,83 @@ +lockfileVersion: '9.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +importers: + + .: + dependencies: + htmlparser2: + specifier: ^9.1.0 + version: 9.1.0 + mkdirp: + specifier: ^3.0.1 + version: 3.0.1 + slug: + specifier: ^9.1.0 + version: 9.1.0 + +packages: + + dom-serializer@2.0.0: + resolution: {integrity: sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==} + + domelementtype@2.3.0: + resolution: {integrity: sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==} + + domhandler@5.0.3: + resolution: {integrity: sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==} + engines: {node: '>= 4'} + + domutils@3.1.0: + resolution: {integrity: sha512-H78uMmQtI2AhgDJjWeQmHwJJ2bLPD3GMmO7Zja/ZZh84wkm+4ut+IUnUdRa8uCGX88DiVx1j6FRe1XfxEgjEZA==} + + entities@4.5.0: + resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==} + engines: {node: '>=0.12'} + + htmlparser2@9.1.0: + resolution: {integrity: sha512-5zfg6mHUoaer/97TxnGpxmbR7zJtPwIYFMZ/H5ucTlPZhKvtum05yiPK3Mgai3a0DyVxv7qYqoweaEd2nrYQzQ==} + + mkdirp@3.0.1: + resolution: {integrity: sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg==} + engines: {node: '>=10'} + hasBin: true + + slug@9.1.0: + resolution: {integrity: sha512-ioOsCfzQSu+D6NZ8XMCR8IW9FgvF8W7Xzz56hBkB/ALvNaWeBs2MUvvPugq3GCrxfHPFeK6hAxGkY/WLnfX2Lg==} + hasBin: true + +snapshots: + + dom-serializer@2.0.0: + dependencies: + domelementtype: 2.3.0 + domhandler: 5.0.3 + entities: 4.5.0 + + domelementtype@2.3.0: {} + + domhandler@5.0.3: + dependencies: + domelementtype: 2.3.0 + + domutils@3.1.0: + dependencies: + dom-serializer: 2.0.0 + domelementtype: 2.3.0 + domhandler: 5.0.3 + + entities@4.5.0: {} + + htmlparser2@9.1.0: + dependencies: + domelementtype: 2.3.0 + domhandler: 5.0.3 + domutils: 3.1.0 + entities: 4.5.0 + + mkdirp@3.0.1: {} + + slug@9.1.0: {} diff --git a/04-asynchronous-control-flow-patterns-with-callbacks/06-web-spider-v3/spider-cli.js b/04-asynchronous-control-flow-patterns-with-callbacks/06-web-spider-v3/spider-cli.js new file mode 100644 index 0000000..abe2be6 --- /dev/null +++ b/04-asynchronous-control-flow-patterns-with-callbacks/06-web-spider-v3/spider-cli.js @@ -0,0 +1,13 @@ +import { spider } from './spider.js' + +const url = process.argv[2] +const maxDepth = Number.parseInt(process.argv[3], 10) || 1 + +spider(url, maxDepth, err => { + if (err) { + console.error(err) + process.exit(1) + } + + console.log('Downloaded complete') +}) diff --git a/04-asynchronous-control-flow-patterns-with-callbacks/06-web-spider-v3/spider.js b/04-asynchronous-control-flow-patterns-with-callbacks/06-web-spider-v3/spider.js new file mode 100644 index 0000000..cd1ea23 --- /dev/null +++ b/04-asynchronous-control-flow-patterns-with-callbacks/06-web-spider-v3/spider.js @@ -0,0 +1,102 @@ +import { readFile, writeFile } from 'node:fs' +import { dirname } from 'node:path' +import { + exists, + get, + getPageLinks, + recursiveMkdir, + urlToFilename, +} from './utils.js' + +function saveFile(filename, content, cb) { + recursiveMkdir(dirname(filename), err => { + if (err) { + return cb(err) + } + writeFile(filename, content, cb) + }) +} + +function download(url, filename, cb) { + console.log(`Downloading ${url} into ${filename}`) + get(url, (err, content) => { + if (err) { + return cb(err) + } + saveFile(filename, content, err => { + if (err) { + return cb(err) + } + cb(null, content) + }) + }) +} + +function spiderLinks(currentUrl, body, maxDepth, cb) { + if (maxDepth === 0) { + // Remember Zalgo from Chapter 3? + return process.nextTick(cb) + } + + const links = getPageLinks(currentUrl, body) + if (links.length === 0) { + return process.nextTick(cb) + } + + let completed = 0 + let hasErrors = false + + function done(err) { + if (err) { + hasErrors = true + return cb(err) + } + if (++completed === links.length && !hasErrors) { + return cb() + } + } + + for (const link of links) { + spider(link, maxDepth - 1, done) + } +} + +export function spider(url, maxDepth, cb) { + const filename = urlToFilename(url) + + exists(filename, (err, alreadyExists) => { + if (err) { + // error checking the file + return cb(err) + } + + if (alreadyExists) { + if (!filename.endsWith('.html')) { + // ignoring non-HTML resources + return cb() + } + + readFile(filename, 'utf8', (err, fileContent) => { + if (err) { + // error reading the file + return cb(err) + } + return spiderLinks(url, fileContent, maxDepth, cb) + }) + } else { + // The file does not exist, download it + download(url, filename, (err, fileContent) => { + if (err) { + // error downloading the file + return cb(err) + } + // if the file is an HTML file, spider it + if (filename.endsWith('.html')) { + return spiderLinks(url, fileContent.toString('utf8'), maxDepth, cb) + } + // otherwise, stop here + return cb() + }) + } + }) +} diff --git a/04-asynchronous-control-flow-patterns-with-callbacks/06-web-spider-v3/utils.js b/04-asynchronous-control-flow-patterns-with-callbacks/06-web-spider-v3/utils.js new file mode 100644 index 0000000..be35ad1 --- /dev/null +++ b/04-asynchronous-control-flow-patterns-with-callbacks/06-web-spider-v3/utils.js @@ -0,0 +1,86 @@ +import { constants, access } from 'node:fs' +import { extname, join } from 'node:path' +import { Parser } from 'htmlparser2' +import { mkdirp } from 'mkdirp' +import slug from 'slug' + +export function exists(filePath, cb) { + access(filePath, constants.F_OK, err => { + if (err) { + if (err.code === 'ENOENT') { + // the file does not exist + return cb(null, false) + } + // unexpected error checking the file + return cb(err) + } + + // the file exists + return cb(null, true) + }) +} + +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 callback-based API because at this point of the book we want +// to demonstrate callback based patterns +export function get(url, cb) { + 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 => cb(null, Buffer.from(content))) + .catch(err => cb(err)) +} + +// NOTE: this function is just for illustrative purposes. We are wrapping +// mkdirp in a callback-based API because at this point of the book we want +// to demonstrate callback based patterns +export function recursiveMkdir(path, cb) { + mkdirp(path) + .then(() => cb(null)) + .catch(e => cb(e)) +} + +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 +} diff --git a/04-asynchronous-control-flow-patterns-with-callbacks/07-callback-unlimited-parallel-execution-pattern/README.md b/04-asynchronous-control-flow-patterns-with-callbacks/07-callback-unlimited-parallel-execution-pattern/README.md new file mode 100644 index 0000000..4cf4863 --- /dev/null +++ b/04-asynchronous-control-flow-patterns-with-callbacks/07-callback-unlimited-parallel-execution-pattern/README.md @@ -0,0 +1,11 @@ +# 07-callback-unlimited-parallel-execution-pattern + +Simple example that demonstrates the callback parallel execution pattern + +## Run + +Run: + +```bash +node index.js +``` diff --git a/04-asynchronous-control-flow-patterns-with-callbacks/07-callback-unlimited-parallel-execution-pattern/index.js b/04-asynchronous-control-flow-patterns-with-callbacks/07-callback-unlimited-parallel-execution-pattern/index.js new file mode 100644 index 0000000..7d04abc --- /dev/null +++ b/04-asynchronous-control-flow-patterns-with-callbacks/07-callback-unlimited-parallel-execution-pattern/index.js @@ -0,0 +1,30 @@ +function makeSampleTask(name) { + return cb => { + console.log(`${name} started`) + setTimeout(() => { + console.log(`${name} completed`) + cb() + }, Math.random() * 2000) + } +} + +const tasks = [ + makeSampleTask('Task 1'), + makeSampleTask('Task 2'), + makeSampleTask('Task 3'), + makeSampleTask('Task 4'), +] + +let completed = 0 +for (const task of tasks) { + task(() => { + if (++completed === tasks.length) { + finish() + } + }) +} + +function finish() { + // all the tasks completed + console.log('All tasks executed!') +} diff --git a/04-asynchronous-control-flow-patterns-with-callbacks/07-callback-unlimited-parallel-execution-pattern/package.json b/04-asynchronous-control-flow-patterns-with-callbacks/07-callback-unlimited-parallel-execution-pattern/package.json new file mode 100644 index 0000000..d23fc08 --- /dev/null +++ b/04-asynchronous-control-flow-patterns-with-callbacks/07-callback-unlimited-parallel-execution-pattern/package.json @@ -0,0 +1,19 @@ +{ + "name": "07-callback-unlimited-parallel-execution-pattern", + "version": "1.0.0", + "description": "Simple example that demonstrates the callback parallel execution pattern", + "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" + } +}