Uploaded by tiniumoh

Nodejs230722

advertisement
What is the DOM? The Document Object Model Explained in
Plain English
D.M. Oladele
Modern web pages are dynamic. This means we need a suitable and
convenient way to modify and manipulate a web document's structure.
This modification in an HTML document, for instance, usually takes the form of creating, adding,
or removing elements in the document.
In this article, you'll learn what the Document Object Model (DOM) is, a bit about its history,
and how you use it to manipulate web documents, especially HTML documents.
What is the Document Object Model (DOM)?
The DOM is a web interface, developed and released by the World Wide Web Consortium
(W3C). This organization was founded to establish standards for the World Wide Web.
The DOM is a web API that is language-neutral. This means that you can implement and adopt it
in any programming language.
The DOM represents the structural pieces of a web document as objects that can be accessed and
manipulated. In other words, the DOM allows you as a software developer to do the following:
•
Create and build web documents.
•
Navigate the structure of web documents.
•
Add, modify, or delete elements and content within web documents.
History of the DOM
The history of the DOM is relative to JavaScript and JScript as the first widely used scripting
languages. These languages helped make web pages interactive.
Prior to the development of a standard DOM specification by the W3C, JavaScript and JScript
had different ways of enabling access to manipulating HTML documents.
These limited methods and interfaces that let you manipulate HTML documents in this way
became the DOM Level 0.
In 1998, the W3C completed its draft of the first standard DOM specification, which became the
recommended standard for all browsers. This standard DOM specification became DOM Level 1.
The DOM level 1 provided a comprehensive model for manipulating both HTML and XML
documents.
In 2000, the W3C released DOM Level 2, which introduced methods such as getElementById(),
as well as a standardized event model and support for XML namespaces and CSS.
The DOM Level 3, released in 2004, added support for XPath and keyboard event handling. And
in late 2015, the latest DOM specification, DOM Level 4, became a published standard.
What is the DOM Tree?
The structural representation created by the DOM is very much like a tree. It has several objects
in it known as nodes.
The browser uses the DOM tree representation it builds from an HTML document to determine
what to render on a web page. For example, a visual representation of a DOM tree will look like
this:
The DOM Tree
The HTML document of the above DOM tree looks like this:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>TITLE</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<div>
<h1>HELLO WORLD</h1>
</div>
<a href="link text">Document Object Model</a>
</body>
</html>
Nodes vs Elements in the DOM
Often, developers confuse nodes with elements. So we should distinguish between the two early
in this article to avoid confusion.
Nodes are all the components a web page document is made up of. In other words, a web page is
collection of nodes.
An element is a type of node within a document. For instance, the DOM
property nodes.childNodes when used on a parent node will return all the different nodes
contained within that specified parent node.
In the example below, the childNodes property is used on the <body> element node of the HTML
document given above:
//javascript content
//select the <body> element node with the DOM method querySelector
const body = document.querySelector('body')
//select the children nodes with the <body> element node with the DOM property node.childNodes
const childrenNodes = body.childNodes
//console log the children nodes
console.log(childrenNodes)//NodeList(5) [text, div, text, a, text]
Notice that there are five items in the nodeList. This is because we have another type of node, the
text nodes, different from the element nodes within the <body> element node.
To investigate this further, go through the following steps in your console:
1. Click on the dropdown icon just before "nodeList".
2. Select the text node by clicking on the dropdown icon before "test".
3. Check for the textContent option within the list options in the dropdown.
If you follow the above instructions, you will see that the test content of the first text node is
"/n ". This is a text node indicating a new line after the <body> element node, the <div> element
node, and the <a> element node.
Relationships Between Nodes in the DOM Tree
The nodes in the DOM tree have a hierarchical relationship with each other in the DOM tree.
They are defined by their position relative to each other in the DOM tree.
These are the node positions present in the DOM tree illustration above:
•
Root node: The root node is always at the apex of the DOM tree. In an HTML document, the
root node is always the <html> element.
•
•
•
•
•
Child node: A child node is a node embedded inside another node. In the illustration given
above, the <head> and the <body> elements are the children of the <html> element.
Descendant node: Any node positioned below another node in the hierarchical order is the
descendant of the nodes positioned above it. For example, although the <h1> element is not the
direct child of the <body> element, it is a descendant of the <body> and root <html> elements.
Parent node: Any node which has another node inside it is a parent node. For example,
the <body> element is the parent of the <div> and <a> elements in the above example. Note that
only element type nodes can be a parent node.
Sibling nodes: Nodes that are on the same level in hierarchical order in the DOM tree are sibling
nodes. For example, <div> and <a> elements in the above example are siblings.
Leaf nodes: The text inside of elements are leaf nodes. This is because they cannot have children
nodes of their own.
HTMLCollection vs nodeList
To manipulate the DOM tree, you need a way to select individual items or a collection of items in
it.
You can use a programming language like JavaScript to select an item or a collection of items in
the DOM tree by using a few methods provided by the DOM.
The methods getElementById() and querySelector() can select individual items. The
methods getElementsByClassName(), getElementsByTagName(), or querySelectorAll() can
select a collection of items.
In the DOM tree, we can either get an HTMLCollection or a NodeList based on the method used
to select a collection of items.
The getElementsByClassName() and getElementsByTagName() methods return
HTMLCollections, while querySelectorAll returns a nodeList.
HTMLCollection and nodeList share some similarities and differences. They're similar in the
following ways:
•
They are array-like objects.
•
They are collections of items.
•
They can be converted into an array by using the Array.from() method.
•
They both have a zero-based indexing.
•
They can both be iterated over with a for...loop.
•
They have a length property.
•
They do not have array methods available to them.
Below is a sample HTML document and JavaScript code to emphasize these similarities:
<!-- html documant -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<ul>
<li>item one</li>
<li>item two</li>
<li>item three</li>
<li>item four</li>
</ul>
<script src="main.js"></script>
</body>
</html>
//javascript content
const listItemsHtmlCollection = document.getElementsByTagName("li")
console.log(listItemsHtmlCollection) // HTMLCollection(4) [li, li, li, li]
const listItemsNodeList = document.querySelectorAll("li")
console.log(listItemsNodeList) // NodeList(4) [li, li, li, li]
You can see from the above that while the getElementsByTagName returns an HTMLCollection
with items matching the <li> tag specified, the querySelectorAll returns a nodeList.
Now, let's use a for...loop to iterate on both collections:
for(let i = 0; i < listItemsHtmlCollection.length; i++) {
listItemsHtmlCollection[i].style.color = 'red'
}
for(let i = 0; i < listItemsHtmlCollection.length; i++) {
listItemsHtmlCollection[i].style.color = 'red'
}
In both instances the color of the text will be changed to red.
Now let's delete the for...loop iteration and use an array method on map to iterate over both
collections:
listItemsHtmlCollection.map( element => element.style.color = 'red' )
listItemsNodeList.map( element => element.style.color = 'red' )
To use the map array method successfully, you have to convert both items to an array with
the Array.from() method like this:
Array.from(listItemsHtmlCollection).map( element => element.style.color = 'red' )
Array.from(listItemsNodeList).map( element => element.style.color = 'red' )
There are two major ways in which HTMLCollection and a nodeList differ from one another.
They are:
•
•
A nodeList comes with some inbuilt methods and properties not available in an HTMLCollection.
The methods include the forEach() and the entries methods to iterate over a nodeList. The
properties include the keys property and the value property.
An HTMLCollection is always live, while a nodeList can either be live or static. A collection of
nodes is live if a change in the DOM tree updates the collection automatically. If a change in the
DOM tree does not affect the collection, then it is static. DOM changes can be the addition of a
new node or the removal of an existing node. DOM methods such
as getElementById() and getElementsByClassName() return HTMLCollections, which is always
live. The querySelectorAll() method returns a static nodeList.
DOM HTML Methods
The DOM level 1 core, Dom level 2 core, and Dom level 3 core introduced several methods that
allow web developers to manipulate the DOM tree. Some of these methods are the following:
The createElement() DOM method
The createElement() method creates
an element of the type specified as its argument.
//html document
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<ul>
<li>item one</li>
<li>item two</li>
<li>item three</li>
<li>item four</li>
</ul>
<script src="main.js"></script>
</body>
</html>
//javascript content
//select the parent element
const list = document.querySelector('ul')
//create a new element
const listItem = document.createElement('li')
//make the newly created element a child of the parent
list.appendChild('listItem')
console.log(list)
Now check the console for the list console logged. You'll see that there are five <li> elements
now within the <ul> parent element.
The createTextNode() DOM method
The createTextNode() method creates a text node with
add text to the <li> element we created above.
the string specified as its argument. Let's
//javascript content
const listText = document.createTextNode("item five")
listItem.appendChild(listText)
Now save your JavaScript file and reload your webpage.
The appendChild() DOM method
The appendChild() method adds
a node to the end of the list of children of a parent node.
If the specified child is an existing node in the document, appendChild() moves it from its
current position on the DOM tree to the new position. We used the method earlier to make our
newly created <li> element a child of the <ul> element.
The getElementById() DOM method
This method selects and returns the element whose ID is specified within it as a argument. If no
such element exists, the method returns null. Let's add an id attribute to our <ul> element in the
HTML document and give it a red border.
//html document
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<ul id="ulList">
<li>item one</li>
<li>item two</li>
<li>item three</li>
<li>item four</li>
</ul>
<script src="main.js"></script>
</body>
</html>
//javascript content
const ulList = document.getElementById("ulList")
ulList.style.border = '2px solid red'
The getElementsByClassName() DOM method
The getElementsByClassName() method selects
all elements with the specified class name and
returns them as an HTMLCollection in the order they appear on the DOM tree.
You can access individual elements in the HTMLCollection by their index number. Let's add a
class attribute to the first two <li> elements in our HTML document and change their text color
to red like this:
//html document
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<ul id="ulList">
<li class="itemOneAndTwo">item one</li>
<li class="itemOneAndTwo">item two</li>
<li>item three</li>
<li>item four</li>
</ul>
<script src="main.js"></script>
</body>
</html>
//javascript content
//select by elements one and two by their class name
const itemOneAndTwo = document.getElementsByClassName("itemOneAndTwo")
//change text color to red with use of index
itemOneAndTwo[0].style.color = 'red'
itemOneAndTwo[1].style.color = 'red'
The getElementsByTagName() DOM method
The getElementsByTagName() method returns an HTMLCollection of all the elements with the
tag name specified as its argument, in the order they appear on the DOM tree.
Let's select the <li> elements with the getElementsBytagName() method and change their font
style to italic.
//javascript content
const liTags = document.getElementsByTagName("li")
for(let i = 0; i < liTags.length; i++) {
liTags[i].style.fontStyle = 'italic'
}
The querySelector() DOM method
The querySelector() method accepts
any CSS string selector as an argument. It then uses the
specified selector to select the first within the document that matches that specified selector.
Let's change select our first two <li> elements with the querySelector() method and change
their text color back to black.
//javascript content
const querySelectItem = document.querySelector("itemOneAndTwo")
querySelectItem.style.color = 'black'
Notice that only the first list item has had its color changed to black.
The querySelectorAll() DOM method
The querySelectorAll() method, just
like the querySelector method, accepts any CSS string
selector as its argument. It then uses the specified CSS string selector to select all elements that
match that specified selector, put them in a nodeList, and return that nodeList.
Now, let's use it to change all the text in out list items to green.
//javascript content
const querySelectAllItems = document.querySelectorAll("li")
for(let i = 0; i < querySelectAllItems.length; i++) {
querySelectAllItems[i].style.color = 'green'
}
The setAttribute() DOM method
The setAttribute() method adds a new attribute name to an element. If an attribute with that
name is already present in the element, its value will change to that of the value set in the
argument.
The method accepts two arguments. The first argument is the name of the attribute you want to
create. The second argument is the value to set on the attribute, which is always a string.
Let's use it to give our third item a class attribute and change the text color to black.
//javascript content
const itemThree = querySelectAllItems[2]
itemThree.setAttribute("class", "attributeValue")
const attributeValue = document.querySelector('.attributeValue')
attributeValue.style.color = 'black'
The removeAttribute() DOM method
The removeAttribute() method removes
a specified attribute. It takes in the name of the
attribute to be removed as its argument. Let's remove the id attribute from the <ul> parent
element and use the removed id to removed its red border.
//javascript content
//remove attribute
ul.removeAttribute('id')
ul.style.border = 'none'
Now save your JavaScript file and reload your web page. Notice that the borders are still there. If
you check the console you will see an error message stating that ul is no longer defined.
The contains() DOM method
The contains() method returns
true if a node is a descendant of a node and returns false
otherwise.
<--HTML document-->
<body>
<h1>Heading</h1>
</body>
//javascript content
const body = document.querySelector('body')
const h1Element = document.querySelector('h1')
console.log( body.contains(h1Element) ) // true
The item() DOM method
The item() method returns
the item specified at the index specified as its argument when used on
a collection.
<--HTML document-->
<body>
<p>Paragraph</p>
<p>Paragraph</p>
</body>
//javascript content
const pElements = document.querySelectorAll("p")
console.log(pElements.item(0)) // <p></p>
The hasChildNodes() DOM method
The hasChildNodes method returns true if the element it is called on has child nodes within it and
returns false otherwise.
<--HTML document-->
<body>
<p>Paragraph</p>
<p>Paragraph</p>
</body>
//javascript content
const body = document.querySelector("body")
console.log(body.hasChildNodes()) // true
What are DOM Events?
To make our web page logically interactive by initiating automatic responses or incidents on the
web page, we need Events.
DOM events are:
actions or occurrences that happen in the system you are programming, which the system tells
you about so your code can react to them. (Source: MDN)
A common example of an event is when a user clicks a submit button in a form, which then
submits the data input by the user as a response to the click.
Another example is when a user clicks on a menu icon, which then triggers a drop-down
navigation or options.
You can use scripting languages such as JavaScript to register event handlers or listeners on
elements inside the DOM tree, which runs when the specified event fires.
An event handler is a:
block of code (usually a JavaScript function that you as a programmer create) that runs when the
event fires. When such a block of code is defined to run in response to an event, we say we
are registering an event handler. (Source: MDN)
Examples of events used on elements in the DOM tree include:
•
•
•
•
•
click: A click event is a mousedown or mouseup over an element on a webpage.
keypress: A keypress event occurs when keys on the keyboard are pressed.
mouseover: A mouseover event occurs when the pointing device is moved onto an element.
dblclick: A dblclick occurs when there is a double-click event over an element on a webpage.
submit: A submit event occurs when a form is submitted.
Conclusion
The DOM is the backbone of modern web dynamism. It represents every piece of a web
document as an object and provides programming languages with the necessary methods to
manipulate and modify each piece.
If you enjoyed this write-up, you should give me a shoutout.
References and Further Reading
1.
2.
3.
4.
5.
https://dom.spec.whatwg.org/
https://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core.html
https://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-html.html
https://www.w3.org/TR/DOM-Level-2-HTML/
https://www.w3.org/TR/DOM-Level-3-Core/core.html
How to Check if String is Empty in JavaScript
Learn multiple ways to easily check if a string is empty in JavaScript.
1. Comparing the String with an Empty String
To check if a string is empty in JavaScript, we can compare the string with an empty string ('') in
an if statement.
For example:
function checkIfEmpty(str) {
if (str === '') {
console.log('String is empty');
} else {
console.log('String is NOT empty');
}
}const str1 = 'not empty';
const str2 = ''; // emptycheckIfEmpty(str1); // outputs: String is NOT empty
checkIfEmpty(str2); // outputs: String is empty
To treat a string containing only whitespace as empty, call the trim() method on the string before
comparing it with an empty string.
function checkIfEmpty(str) {
if (str.trim() === '') {
console.log('String is empty');
} else {
console.log('String is NOT empty');
}
}const str1 = 'not empty';
const str2 = ''; // empty
const str3 = '
'; // contains only whitespacecheckIfEmpty(str1); // outputs: String is NOT
empty
checkIfEmpty(str2); // outputs: String is empty
checkIfEmpty(str3); // outputs: String is empty
The String trim() method removes all whitespace from the beginning and end of a string and
returns a new string, without modifying the original.
const str1 = ' bread ';
const str2 = '
milk tea
';console.log(str1.trim()); // 'bread'
console.log(str2.trim()); // 'milk tea'
Tip
Trimming a string when validating required fields in a form helps ensure that the user entered
actual data instead of just whitespace.
How to Check if a String is Empty, null, or undefined
Depending on your scenario, you might want to consider that the string could be a nullish value
(null or undefined). To check for this, use the string if statement directly, like this:
function checkIfEmpty(str) {
if (str) {
console.log('String is NOT empty');
} else {
console.log('String is empty');
}
}const str1 = 'not empty';
const str2 = ''; // empty
const str3 = null;
const str4 = undefined;checkIfEmpty(str1); // outputs: String is NOT empty
checkIfEmpty(str2); // outputs: String is empty
checkIfEmpty(str3); // outputs: String is empty
checkIfEmpty(str4); // outputs: String is empty
If the string is nullish or empty, it will be coerced to false in the if statement. Otherwise, it will be
coerced to true.
To remove all whitespace and also check for a nullish value, use the optional chaining operator (?.)
to call the trim() method on the string before using it in an if statement.
function checkIfEmpty(str) {
if (str?.trim()) {
console.log('String is NOT empty');
} else {
console.log('String is empty');
}
}const str1 = 'not empty';
const str2 = ''; // empty
const str3 = null;
const str4 = undefined;
const str5 = '
'; // contains only whitespacecheckIfEmpty(str1); // outputs: String is NOT
empty
checkIfEmpty(str2); // outputs: String is empty
checkIfEmpty(str3); // outputs: String is empty
checkIfEmpty(str4); // outputs: String is empty
checkIfEmpty(str5); // outputs: String is empty
The optional chaining operator lets us call the trim() method on a null or undefined string without
causing an error. Instead, it prevents the method call and returns undefined.
const str1 = null;
const str2 = undefined;console.log(str1?.trim()); // undefined
console.log(str2?.trim()); // undefined
2. Comparing the Length of the String with 0
Alternatively, we can access the length property of a string and compare its value with 0 to check if
the string is empty.
function checkIfEmpty(str) {
if (str.length === 0) {
console.log('String is empty');
} else {
console.log('String is NOT empty');
}
}const str1 = 'not empty';
const str2 = ''; // emptycheckIfEmpty(str1); // outputs: String is NOT empty
checkIfEmpty(str2); // outputs: String is empty
To check for strings containing only whitespace with this approach, we would also call
the trim() method before comparing the length of the trimmed string with 0.
function checkIfEmpty(str) {
if (str.trim().length === 0) {
console.log('String is empty');
} else {
console.log('String is NOT empty');
}
}const str1 = 'not empty';
const str2 = ''; // empty
const str3 = '
'; // contains only whitespacecheckIfEmpty(str1); // outputs: String is NOT
empty
checkIfEmpty(str2); // outputs: String is empty
checkIfEmpty(str3); // outputs: String is empty
Updated at: codingbeautydev.com
Every Crazy Thing JavaScript Does
A captivating guide to the subtle caveats and lesser-known parts of JavaScript.
Sign up and receive a free copy immediately.
7 Console Methods Used by Pros
Often while debugging, beginners use the console.log() method to print out values. But there are
a few other console methods that make your life much easier. Want to know what these
methods are? Let's dive in!
1. console.table()
Logging matrixes or even long arrays or objects is a headache using
the console.log() method. console.table() is a much more elegant way to do it.
// Matrix
console.table([
["apple", "banana", "cherry"],
["Rs 80/kg", "Rs 100/kg", "Rs 120/kg"],
["5 ⭐", "4 ⭐", "4.5 ⭐"],
]);// Maps
class Person {
constructor(firstName, lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
}const family = {};
family.mother = new Person("Jane", "Smith");
family.father = new Person("John", "Smith");
family.daughter = new Person("Emily", "Smith");console.table(family);
2. console.trace()
Are you having issues debugging a function? Left wondering how the execution
flows? console.trace() is your friend!
function outerFunction() {
function innerFunction() {
console.trace();
} innerFunction();
}outerFunction();
3. console.error() and console.warn()
Tired of boring logs? Spice things up with console.error() and console.warn().
console.error("This is an error message");
console.warn("This is a warning message");
console.log("This is a log message");
4. console.assert()
This is another brilliant tool for debugging! If the assertion fails, the console will print out
the trace.
function func() {
const a = -1;
console.assert(a === -1, "a is not equal to -1");
console.assert(a >= 0, "a is negative");
}func();
5. console.count() and console.countReset()
Yet another incredible debugging tool! console.count() will print out the number of times it is
executed.
function fibonacci(num) {
console.count("fibonacci");
if (num < 2) {
return num;
}
return fibonacci(num - 1) + fibonacci(num - 2);
}fibonacci(2);console.countReset("fibonacci");
console.log("COUNTER RESET");fibonacci(5);
6. console.time(), console.timeEnd(), and console.timeLog()
Need to check how long something takes? The timer methods are there to rescue you!
console.time("timeout-timer");setTimeout(() => {
console.timeEnd("timeout-timer");
}, 1000);setTimeout(() => {
console.timeLog("timeout-timer");
}, 500);
NOTE: The setTimeouts are not executed immediately, resulting in a small deviation from the
expected time.
7. console.clear()
After logging so much to the console, of course, you would need to clear it up for further use.
The console.clear() method is the way to go!
console.log("Some random text");
console.clear();
That’s all folks! Hope this helps you become a better, well-rounded developer!
Research says, writing down your goals on pen & paper makes you 21% to 39% more likely
to achieve them. Check out these notebooks and journals to make the journey of achieving
your dreams easier: https://www.amazon.com/Tapajyoti-Bose/e/B09VGDDHRR
Follow me for weekly new tidbits on the domain of tech!
Need a Top Rated Front-End Development Freelancer to chop away your development
woes? Contact me on Upwork
Want to see what I am working on? Check out my Personal Website and GitHub
Want to connect? Reach out to me on LinkedIn
Follow me on Instagram to check out what I am up to recently.
ES6 way to clone an array
Hello Folks
What’s up friends, this is SnowBit here. I am a young, passionate and self-taught developer and
have an intention to become a successful developer.
I hope you enjoy reading this article.
In the olden days, when ES6 was not introduced, we often use the slice() method to clone an
array. Now it's the time for ES6, you can use the spread operator to clone an array. It looks pretty
neat and right.
const ducks = [" ", " ", " ", " "]
// Old way
const ducksClone = ducks.slice()
// ES6 way
const ducksCloneES6 = [...ducks]
This is how you clone an array with ES6.
But your crazy mind would have wondered…
Why can’t I use = to clone an array?
This is because the array in JavaScript is only referenced values so when you put = and try to clone
an array will only copy the reference of the original array to a variable and not an array.
const ducks = [" ", " ", " "]
const pirateDucks = ducks
const cloneDucks = [...ducks]
console.log(ducks === cloneDucks)
// true -> same memory space
console.log(ducks === pirateDucks)
// false -> new memory space
Some problems arise when using = to clone array
In Javascript, arrays are mutable i.e their state can be modified. So, this might happen when
using =
const ducks = [" ", " ", " "]
const pirateDucks = ducks
pirateDucks.push(" ☠ ")
console.log(pirateDucks)
// [" ", " ", " ", " ☠ "]
console.log(ducks)
// [" ", " ", " ", " ☠ "] - Original duck array values got changed
Thank you for reading, have a nice day! Your appreciation is my motivation
•
Blog — Check out my blog
•
Follow me on Twitter — @codewithsnowbit
•
Subscribe to me on YouTube — Code With SnowBit
JavaScript Interview Questions that made me think — Do
I really know JavaScript?
Photo by Rahul Mishra on Unsplash
The best part of JavaScript which always amaze me is that there is always something new
happening around it. And no matter how much you know about it, you will always get to learn new
things about it.
I’ve collected these questions over a period of quite long time. In most of the questions I really had
no idea about what the output would be, until I tried them by myself.
So here I am documenting them so that other people can make use of it to learn new concepts:
.
.
.
Output:
21
{name: “Lydia”} // age won’t be included. Because property defined with defineProperty are
non enumerable by default.
.
.
.
Output:
false // delete operator only deletes a key in object
true // when we don't use any declaration before any variable, it will be treated as a global
variable, and will be added as deletable entity in window object.undefined
.
.
.
Output:
[ { name: "Noren Red"} ] Initially I thought it will log [ null ] because we have initialised
person with nullBut in reality, we are only setting new reference to person variable.
Previous reference will be used in members arrayIn Short, { name: "Noren Red"} lives in some
memory space whose address is X201and this is how referencing is workinglet person = X201
const members = [ X201 ]
person = null
.
.
.
Output
Silver Surfer Because when we return a property, the value of the property is equal to the
returned value, not the value set in the constructor function.
Output:
With the padStart method, we can add padding to the beginning of a string.
The value passed to this method is the total length of the string together with the padding.
The string "Silver Surfer" has a length of 13. name.padStart(14) inserts 1 space at the start
of the string, because 13 + 1 is 13. If the argument passed to the padStart method is smaller
than the length of the array, no padding will be added.
.
.
.
Output
777If we pass string and number combination to parseInt, what parseInt does is, it check at
which position wrong datatype is getting started, if value before the wrong datatype is a
valid number, it will return the valid number.
.
.
.
Output:
1 2 and undefined 3 and undefined 4
If we don't pass initial value, then by default x will be first value of array, and y will be
second value of array.
.
.
.
Output:
one - ["", " is ", " years old"]
two - Thor
three - 1000one - ["hey there, are you amazed"]
two - undefined
three - undefinedIf we use tagged template literals, the value of the first argument is
always an array of the string values.The remaining arguments get the values of the passed
expressions!
.
.
.
Output:
1
undefined
2
.
.
.
Output
function // Classes in JS are functions under the hood
kehey74, CC BY-SA 4.0, via Wikimedia Commons
Using the at() Method in JavaScript
Quickly Get the Last Item of an Array
Connect with us. Want to hear what’s new at The Pragmatic Bookshelf? Sign up for our
newsletter. You’ll be the first to know about author speaking engagements, books in beta, new
books in print, and promo codes that give you discounts of up to 40%.
A programmer’s daily tasks often involve making, modifying, or returning arrays. These simple yet
essential operations play an important role in JavaScript applications. That’s why ECMAScript
keeps adding cool new array methods such as flat() and flatMap() to make working with arrays
easier.
A recently introduced feature that hasn’t received the attention it deserves is the at() method —
mostly because it’s already possible to get the same result using the square bracket notation ([]).
Consider the following example:
const arr = ['first', 'second', 'last'];console.log(arr[0]);
console.log(arr.at(0));
// => first
// => first
As with [], at() returns the array item at the given index. But, when it comes to retrieving the last
item in an array, at() is simpler to use. Rather than having to read the length property and
subtracting one from it, we can pass -1. Compare:
const arr = ['first', 'second', 'last'];console.log(arr[arr.length-1]);
console.log(arr.at(-1));
// => last
// => last
When supplying at() with a negative number, it counts back from the end of the array, so if we
pass -2 it gets us the array’s second to last item. If there’s no item at the given index, the return
value will be undefined.
When you need to retrieve the last item in an array consider using at().
If you enjoyed this article, you may also like Faraz Kelhini’s book, Modern Asynchronous
JavaScript. Through March 31, 2022, you can use promo code fkajs_medium_35 to save 35%
on the ebook version. Promo codes are not valid on prior purchases.
Photo by Blake Connally on Unsplash
Debouncing and Throttling in Javascript
Debouncing and throttling are both used to enhance the website performance by limiting the
number of times the events are triggered. Debouncing and throttling are not provided by
JavaScript. They’re just concepts that can be implemented using the setTimeout web API.
What is Debouncing
Let’s take an example. You have opened an e-commerce website to search for laptop bags.
If debounce is not applied you can see in the below image the number of calls is made on every
keystroke.
After implementing debounce, we have significantly reduced the number of calls. Now calls are
made only when the user types again after the specified time. The function will be executed only
when the time taken by the user to type again is equal to the delay that we have provided.
Custom debounce function
What is throttling
Throttling is also used to rate-limit the function call. Throttling will fire the function call only once
in 1000ms(the limit which we have provided), no matter how many times the user fires the
function call.
Custom Throttling function
Conclusion
I hope after reading this article these two concepts by javascript are cleared.
Throttling and debouncing can be implemented to enhance the searching functionality, infinite
scroll, and resizing of the window.
In case I have missed something or you have some advice or suggestions do let me know in
the comment section.
You can also reach out to me
https://www.linkedin.com/in/akhatun/
https://github.com/amnahkhatun
Happy coding✌
Why do we need Streams in Node.js?
Photo by Snejina Nikolova on Unsplash
Streams are sequences of data made available over time. The difference with other types of data
like strings or arrays is that streams might not be available all at once, and they don’t have to fit in
memory.
Many of the built-in modules in Node implement the streaming interface, such as HTTP response,
HTTP request, fs, process.stdin, etc.
Let’s see how Stream solves our slow/blocking web server problem.
Assume we need to serve a big file using a Node web server.
mkdir blog-why-node-streams
cd blog-why-node-streams
echo "" > index.js
Update index.js with the below code,
const fs = require("fs");
const http = require("http");// code to generate random big sized file on fly
fs.stat("big.file", function (err, stat) {
if (err == null) {
console.log("File exists");
} else if (err.code === "ENOENT") {
const file = fs.createWriteStream("./big.file");
for (let i = 0; i <= 1e6; i++) {
file.write(
`Lorem ipsum dolor sit amet, consectetur adipiscing elit. Curabitur tempus id metus a
sodales. Maecenas faucibus bibendum mauris elementum ultrices. In hac habitasse platea
dictumst. Pellentesque consequat augue nec urna interdum, a sagittis arcu ornare. Duis
pulvinar odio vitae velit euismod, nec pretium nisi tempus. Lorem ipsum dolor sit amet,
consectetur adipiscing elit. Cras ante lorem, suscipit non lobortis venenatis, interdum a
dui. Donec rhoncus magna lectus, ut vestibulum eros rutrum gravida. Aenean sit amet fringilla
erat. In varius fermentum justo, in maximus sapien tempus non. Sed malesuada tempor erat eget
tristique. Pellentesque diam nulla, pharetra sed luctus nec, euismod non tortor.`
);
}
console.log("big.file created");
file.end();
} else {
console.log("Some other error: ", err.code);
}
});const server = http.createServer();server.on("request", (req, res) => {
fs.readFile("./big.file", (err, data) => {
if (err) throw err;
res.end(data);
});
});server.listen(8000, () => console.log("The server is running at localhost:8000"));
The above code does two things:
1. The first part of the code is to generate a huge (~600 MB) file. A utility code.
2. The second part is a simple web server endpoint serving the big.file file.
Let’s run the server.
> node index.jsThe server is running at localhost:8000
big.file created
After starting the node server, let’s see the memory usage using the Windows task manager. We
have ~5.8 MB of memory consumed by our server.
Node server memory consumption before serving the file
Now let’s curl the endpoint to download the file.
> curl localhost:8000Lorem ipsum dolor sit amet, consectetur adipiscing e......
............................
.......................
Now, look at the memory consumption for the server using task manager.
Node server memory consumption while serving the file
When we run the server, it starts out with a normal amount of memory, ~5.8 MB. Then we
connected to the server. Note what happened to the memory consumed. The memory
consumption jumped to ~684 MB.
How does it work?
We basically put the whole big.file content in memory before we wrote it out to the response object.
This is very inefficient.
Solution,
The HTTP response object (res in the code above) is also a writable stream. This means that if we
have a readable stream that represents the content of big.file, we can simply pipe those two
together and get nearly the same result without consuming ~ 684 MB of memory.
Node’s fs module can give us a readable stream for any file using the createReadStream method. We
can pipe that to the response object.
So, replace the request handler code with the below code snippet and measure the memory
consumption.
server.on("request", (req, res) => {
const src = fs.createReadStream("./big.file");
src.pipe(res);
});
Let’s run our server again,
> node index.jsThe server is running at localhost:8000
big.file created
Now, let’s curl the endpoint,
> curl localhost:8000
> curl localhost:8000Lorem ipsum dolor sit amet, consectetur adipiscing e......
............................
.......................
Now, look at the memory consumption for the server in the task manager.
Node server memory consumption while serving the file with streaming chunks
When we ran the server, it started out with a normal amount of memory, ~ 5.8 MB. Then we
connected to the server (curl). Note what happened to the memory consumed. The memory
consumption is just ~8 MB.
Now, what’s changed, and how is it working?
When a client asks for that big file, we stream it one chunk at a time, which means we
don’t buffer it in memory at all. The memory usage grew by about ~8 MB and that’s it.
These scenarios need not be for just an HTTP server; they may be applicable to cases such as file
content manipulation, big file creation, uploading files from client to server, or sending big audio
or video file to a client, etc.
Error Handling in Node.js Like a Pro
All you need to know to get started.
Photo by ThisisEngineering RAEng on Unsplash
Handling errors are one of the most important aspects of any production-grade application.
Anyone can code for the success cases. Only true professionals take care of the error cases.
Today we will learn just that. Let’s dive in.
First, we have to understand that not all errors are the same. Let’s see how many types of errors
can occur in an application.
•
User Generated Error
•
Hardware failure
•
Runtime Error
•
Database Error
We will see how we can easily handle these different types of errors.
This article is part of a series where I am building a ExpressJSBoilerplate from Scratch. You
can check that here
Get a basic express application
Run the following command to get a basic express application built with typescript.
git clone https://github.com/Mohammad-Faisal/express-typescript-skeleton.git
Handle not found URL errors
How do you detect if a hit URL is not active in your express application? You have an URL
like /users, but someone is hitting /user. We need to inform them that the URL they are trying to
access does not exist.
That’s easy to do in ExpressJS. After you define all the routes, add the following code to catch all
unmatched routes and send back a proper error response.
app.use("*", (req: Request, res: Response) => {
const err = Error(`Requested path ${req.path} not found`);
res.status(404).send({
success: false,
message: "Requested path ${req.path} not found",
stack: err.stack,
});
});
Here we are using “*” as a wildcard to catch all routes that didn’t go through our application.
Handle all errors with a special middleware
Now we have a special middleware in Express that handles all the errors for us. We have to include
it at the end of all the routes and pass down all the errors from the top level so that this
middleware can handle them for us.
The most important thing to do is keep this middleware after all other middleware and route
definitions because otherwise, some errors will slip away.
Let’s add it to our index file.
app.use((err: Error, req: Request, res: Response, next: NextFunction) => {
const statusCode = 500;
res.status(statusCode).send({
success: false,
message: err.message,
stack: err.stack,
});
});
Have a look at the middleware signature. Unline other middleware, This special middleware has
an extra parameter named err, which is of the Error type. This comes as the first parameter.
And modify our previous code to pass down the error like the following.
app.use("*", (req: Request, res: Response, next: NextFunction) => {
const err = Error(`Requested path ${req.path} not found`);
next(err);
});
Now, if we hit a random URL, something like, http://localhost:3001/posta, then we will get a
proper error response with the stack.
{
"success": false,
"message": "Requested path ${req.path} not found",
"stack": "Error: Requested path / not found\n
at
/Users/mohammadfaisal/Documents/learning/express-typescript-skeleton/src/index.ts:23:15\n"
}
Custom error object
Let’s have a closer look at the default error object provided by Node.js.
interface Error {
name: string;
message: string;
stack?: string;
}
So when you are throwing an error like the following.
throw new Error("Some message");
Then you are only getting the name and the optional stack properties with it. This stack provides
us with info on where exactly the error was produced. We don't want to include it in production.
We will see how to do that later.
But we may want to add some more information to the error object itself.
Also, we may want to differentiate between various error objects.
Let’s design a basic Custom error class for our application.
export class ApiError extends Error {
statusCode: number;
constructor(statusCode: number, message: string) {
super(message);
this.statusCode = statusCode;
Error.captureStackTrace(this, this.constructor);
}
}
Notice the following line.
Error.captureStackTrace(this, this.constructor);
It helps to capture the stack trace of the error from anywhere in the application.
In this simple class, we can append the statusCode as well. Let's modify our previous code like the
following.
app.use("*", (req: Request, res: Response, next: NextFunction) => {
const err = new ApiError(404, `Requested path ${req.path} not found`);
next(err);
});
And take advantage of the new statusCode property in the error handler middleware as well
app.use((err: ApiError, req: Request, res: Response, next: NextFunction) => {
const statusCode = err.statusCode || 500; // <- Look here res.status(statusCode).send({
success: false,
message: err.message,
stack: err.stack,
});
});
Having a custom-defined Error class makes your API predictable for end users to use. Most
newbies miss this part.
Let’s handle application errors
Now let’s throw a custom error from inside our routes as well.
app.get("/protected", async (req: Request, res: Response, next: NextFunction) => {
try {
throw new ApiError(401, "You are not authorized to access this!"); // <- fake error
} catch (err) {
next(err);
}
});
This is an artificially created situation where we need to throw an error. The real life, we may have
many situations where we need to use this kind of try/catch block to catch errors.
If we hit the following URL http://localhost:3001/protected, we will get the following response.
{
"success": false,
"message": "You are not authorized to access this!",
"stack": "Some details"
}
So our error response is working correctly!
Let’s improve on this!
So we now can handle our custom errors from anywhere in the application. But it requires a try
catch block everywhere and requires calling the next function with the error object.
This is not ideal. It will make our code look bad in no time.
Let’s create a custom wrapper function that will capture all the errors and call the next function
from a central place.
Let’s create a wrapper utility for this purpose!
import { Request, Response, NextFunction } from "express";export const asyncWrapper = (fn:
any) => (req: Request, res: Response, next: NextFunction) => {
Promise.resolve(fn(req, res, next)).catch((err) => next(err));
};
And use it inside our router.
import { asyncWrapper } from "./utils/asyncWrapper";app.get(
"/protected",
asyncWrapper(async (req: Request, res: Response) => {
throw new ApiError(401, "You are not authorized to access this!");
})
);
Run the code and see that we have the same results. This helps us to get rid of all try/catch blocks
and call the next function everywhere!
Example of a custom error
We can fine-tune our errors to our needs. Let’s create a new error class for the not found routes.
export class NotFoundError extends ApiError {
constructor(path: string) {
super(404, `The requested path ${path} not found!`);
}
}
And simplify our bad route handler.
app.use((req: Request, res: Response, next: NextFunction) => next(new
NotFoundError(req.path)));
How clean is that?
Now let’s install a small little package to avoid writing the status codes ourselves.
yarn add http-status-codes
And add the status code in a meaningful way.
export class NotFoundError extends ApiError {
constructor(path: string) {
super(StatusCodes.NOT_FOUND, `The requested path ${path} not found!`);
}
}
And inside our route like this.
app.get(
"/protected",
asyncWrapper(async (req: Request, res: Response) => {
throw new ApiError(StatusCodes.UNAUTHORIZED, "You are not authorized to access this!");
})
);
It just makes our code a bit better.
Handle programmer errors.
The best way to deal with programmer errors is to restart gracefully. Place the following line of
code at the end of your application. It will be invoked in case something is not caught in the error
middleware.
process.on("uncaughtException", (err: Error) => {
console.log(err.name, err.message);
console.log("UNCAUGHT EXCEPTION!
Shutting down...");
});
process.exit(1);
Handle unhandled promise rejections.
We can log the reason for the promise rejection. These errors never make it to our express error
handler. For Example, if we want to access a database with the wrong password.
process.on("unhandledRejection", (reason: Error, promise: Promise<any>) => {
console.log(reason.name, reason.message);
console.log("UNHANDLED REJECTION!
Shutting down...");
process.exit(1);
throw reason;
});
Further improvement
Let’s create a new ErrorHandler class to handle the errors in a central place.
import { Request, Response, NextFunction } from "express";
import { ApiError } from "./ApiError";export default class ErrorHandler {
static handle = () => {
return async (err: ApiError, req: Request, res: Response, next: NextFunction) => {
const statusCode = err.statusCode || 500;
res.status(statusCode).send({
success: false,
message: err.message,
rawErrors: err.rawErrors ?? [],
stack: err.stack,
});
};
};
}
This is just a simple error handler middleware. You can add your custom logic here. And use it
inside our index file.
app.use(ErrorHandler.handle());
That’s how we can separate the concerns by respecting the single responsibility principle of
SOLID.
I hope you learned something new today. Have a wonderful rest of your day!
Want to Connect?You can reach out to me via LinkedIN or my Personal Website
Is it time to ditch Svelte, React, and VUE?
Almost every modern web application built these days starts with an enormous clusterf*ck of
JavaScript on the front-end which literally replaces the entire browser viewport with a JSrendered virtual DOM and consumes JSON via a REST API which is built as a separate (but tightly
coupled) application. Sounds kinda crazy, right? Spoiler alert: That’s because it is totally f*cking
crazy!
If you’re building a Single Page Application (SPA) like maybe the next Figma or Trello, then one of
those tools might fit the bill perfectly. But if you’re building a Multi Page Application (MPA) like a
typical e-commerce website or even something like Gmail, I’m here to tell you that using a SPA
framework is likely adding far more complexity than it’s worth.
The trouble with SPA architecture
Using the server only as a “dumb” API means we can no longer easily rely on it to maintain our
application state. So we’ve moved all that state management to the client, inspiring a whole new
category of frameworks like Redux and MobX. And since we can no longer use the server for basic
routing, new libraries like React Router and Page.js were created to simulate the natural routing
functionality we used to get for free.
Authentication used to be trivially easy to implement with server-side sessions. With SPA
architecture, we typically use JSON Web Tokens which are far more difficult to implement (and far
easier to implement badly). Even basic form submission can no longer rely on the browser’s
standard implementation of HTML to submit form fields based on their name attributes. We're now
required to bind those values to a JS object and manage and submit that object "manually".
In other words, all this stuff we used to get for free, now requires quite a lot of extra work. But is it
worth it?
How did we get here?
In the olden days, the web was simple. Your browser sent an HTTP request, the server sent a new
document, and your browser dutifully rendered it to the viewport blowing away whatever was
there before. This was a bit clunky, though. It meant that if you wanted to update just one little
piece of the page, you had to re-render the entire thing. Then JQuery came along which made it
relatively simple to update only parts of the page using AJAX without a full-page refresh and to
build web applications which felt far more interactive and responsive — more “app-like”. But it
involved a lot of imperative JavaScript and was hard to maintain. If you wanted to make
something moderately complex, it didn’t take long before you had an unmaintainable rat’s nest of
JQuery.
Then along came Angular, followed by React and friends with a radical new approach: What if we
re-think the whole concept of a “front-end” not as a DOM sprinkled with JavaScript — but rather
as a JavaScript application which ultimately renders a DOM. Let’s turn it upside down! And it
worked brilliantly if what you wanted to build was a Single Page App. Sure you lost a lot
of the simplicity of a basic client/server architecture with HTML on the wire. But it freed you to
build a truly app-like frontend experience. This new approach was exciting — almost intoxicating.
And before long, every greenfield project looked like a good candidate for SPA.
But user expectations for a modern, reactive website have also increased dramatically over the past
5 or 10 years. So building a “web 1.0” style application with full page reloads just won’t cut it
anymore.
Modern UI without SPA
So how can we build a modern MPA website without using a SPA-frontend / REST-backend
architecture, without writing 80,000 lines of crufty JQuery, and without a janky full-page refresh
on every click like it was built circa 1999?
There’s a new crop of libraries designed to provide modern interactivity while working with the
grain of HTML and HTTP — both of which start with HT for Hypertext. This is key. The web was
designed with the idea of Hypertext going up and down the wire. Not JSON. New libraries like
Hotwire, HTMX, and Unpoly allow you to swap out chunks of your DOM in a declarative way by
adding HTML attibutes or tags to your markup — without writing any JavaScript yourself. For
example, an “Add to Cart” button could send a request to the server which modifies the serverside state of the cart items in your server-side session, then send back two chunks of DOM which
replace only the #cart-sidebar and #cart-icon-badge on the page. This can be done quite elegantly
and with beautiful CSS animations too.
When we send HTML down the wire as God (aka, Tim Berners-Lee) intended, it turns out there’s a
ton of stupid shit we no longer need. Things like client-side state management — the DOM is the
client-side state. Client-side routers? Don’t be ridiculous. JSON Web Tokens? Server sessions are
tried and true — and so much easier to implement. Our database queries become very easy too
since we’re writing all our routes on the server-side where we already have secure, direct access to
the database.
I wrote a simple ExpressJS-based framework to implement this style of architecture which you can
check out here: https://www.sanejs.dev
Ruby on Rails Shines in 2022 (no, really!)
Like most modern web developers, I’ve long shunned Ruby on Rails as a legacy framework
designed to build a style of monolithic web application which is no longer even relevant. But here’s
the thing: If we’re using something like Hotwire or HTMX on the frontend, we can use anything
we want for the backend. Since we’re working with the grain of HTML, ideally, we want the very
best system for creating server-rendered templates. There really aren’t that many full-featured,
batteries-included frameworks out there. The big ones are Rails, Django, and Laravel. There are a
few others up and coming such as Phoenix based on Elixer and Buffalo based on Go. But Rails has
a huge community, is very polished, and is honestly just a joy to work with.
But crucially, the latest Rails 7.0 released last December includes the incredible new Hotwire
library for frontend interactivity. Hotwire can be used with or without Rails — but it’s designed to
pair perfectly with Rails development and is baked-in by default. So believe it or not, in 2022, Rails
may now be the perfect full-stack framework for building post-jamstack era MPA web applications
with modern interactivity that works with the grain of HTML rather than replacing it wholesale
with the clusterf*ck of JS we’ve come to expect on the front-end plus a whole ‘nuther app for the
backend API.
Wrapping it all up
If the ultimate goal is to build modern MPA websites in a way that is fast, organized, and
maintainable, then it’s worth seriously considering whether SPA/Jamstack architecture is really
the right tool for the job. With the arrival of modern DOM-swapping interactivity libraries like
Hotwire, HTMX, and Unpoly, we finally have a real, practical alternative to SPA which allows us to
create modern, elegant interfaces that work with the grain of HTML meaning we don’t have to
reinvent the wheel for basic things like application state management and form submission. So if
we’re going back to server-rendered templates, then maybe it’s time to take another look at the
reigning all-time champion of web frameworks, Ruby on Rails. Especially now that the brand new
7.0 release comes with Hotwire baked-in, Rails just might be the very best solution in 2022 for
building modern Multi Page Applications.
Kafka with Node.Js
Photo by Erlend Ekseth on Unsplash
Kafka is one of the very efficient and popular event/message steaming platforms for building
microservices-based software applications, Kafka can work as the backbone of your microservices
so that different microservices can communicate with each other in an asynchronous fashion.
Integration with Node.JS — We are going to create a simple node.js application to consume
data from Wikimedia event stream API and put it inside a Kafka Topic using a producer and then
read the data from Kafka and store it inside ElasticSearch. below is the HLD of the sample
application
HLD of project
To work with Kafka we will be using the NPM module KafkaJS and to listen to the event stream we
will use eventsource module so let's get started
First, we will create a node project and install npm modules
npm i eventsource kafkajs
Our Project is ready now let's start with listening to the Wikimedia stream
import {
Kafka }
from
'kafkajs';
var EventSource = require('eventsource');
var es = new EventSource('https://stream.wikimedia.org/v2/stream/recentchange');
es.on('message', async (data: any, err: any) => {
const payload = JSON.parse(data.data);
console.log('Received Data: ', payload);
});
the above code will listen to the Wikimedia stream and then when a message is received it will log
in to the console, next step is to send the event inside Kafka so let's write our producer
import {
Kafka,
Producer }
from
'kafkajs';
export const getProducer = async () => {
const kafka = new Kafka({
clientId: 'producer-client',
brokers: ['localhost:9092'],
});
const producer: Producer = kafka.producer();
await producer.connect();
return producer;
};
We can use the above producer to send a message to Kafka using the following code, we will use it
inside our main file
await producer.send({
topic: 'wikimedia.recentchanges',
messages: [{ value: JSON.stringify(payload) }],
});
With producer setup being done now its time to build our consumer which will consume the
message publisher on ‘wikimedia.recentchanges’ topic from Kafka
import { ConsumerSubscribeTopic, Kafka } from 'kafkajs';
export const getConsumer = async () => {
const kafka = new Kafka({
clientId: 'consumer-client',
brokers: ['localhost:9092'],
});
const consumer = kafka.consumer({ groupId: 'my-group' });
const subscription: ConsumerSubscribeTopic = {
topic: 'wikimedia.recentchanges',
fromBeginning: false,
};
await consumer.connect();
await consumer.subscribe(subscription);
return consumer;
};
Now our producer and consumer are ready let's create an index file to run both producer and
consumer
import {
EachMessagePayload,
Message } from
'kafkajs';
import { getConsumer } from './consumer';
import { getProducer } from './producer';
var EventSource = require('eventsource');
var es = new EventSource('https://stream.wikimedia.org/v2/stream/recentchange');
const start = async () => {
const producer = await getProducer();
es.on('message', async (data: any, err: any) => {
const payload = JSON.parse(data.data);
console.log('Received Data: ', payload);
//publish the message to Kafka
await producer.send({
topic: 'wikimedia.recentchanges',
messages: [{ value: JSON.stringify(payload) }],
});
});
// Start of Consumer
const consumer = await getConsumer();
await consumer.run({
eachBatchAutoResolve: false,
eachMessage: async (messagePayload: EachMessagePayload) => {
const { topic, partition, message } = messagePayload;
const prefix = `${topic}[${partition} | ${message.offset}] / ${message.timestamp}`;
console.log(message);
},
});
};
start();
if you run our main file you will see that we are successfully able to send and consume messages to
and from Kafka, now let’s save the message inside ElasticSearch.
To work with elastic search we will be using the npm module @elastic/elasticsearch let's change
our main file to save Wikimedia changes to elastic search
import {
EachMessagePayload
} from 'kafkajs';
const { Client } = require('@elastic/elasticsearch');
import { getConsumer } from './consumer';
import { getProducer } from './producer';
var EventSource = require('eventsource');
var es = new EventSource('https://stream.wikimedia.org/v2/stream/recentchange');
const start = async () => {
const producer = await getProducer();
es.on('message', async (data: any, err: any) => {
const payload = JSON.parse(data.data);
console.log('Received Data: ', payload);
//publish the message to Kafka
await producer.send({
topic: 'wikimedia.recentchanges',
messages: [{ value: JSON.stringify(payload) }],
});
});
//Create the elastic search client
const elsticClient = new Client({
node: 'http://localhost:9200',
});
// Start of Consumer
const consumer = await getConsumer();
await consumer.run({
eachBatchAutoResolve: false,
eachMessage: async (messagePayload: EachMessagePayload) => {
const { topic, partition, message } = messagePayload;
const prefix = `${topic}[${partition} | ${message.offset}] / ${message.timestamp}`;
console.log(message);
// Publish the message to elastic
await elsticClient.index({
index: 'wikimedia_recentchanges',
document: message,
});
},
});
};
start();
Now if you run the main file and open Kibana you will see the documents are getting indexed
inside ElasticSearch index ‘wikimedia_recentchanges’
The complete source code of the project is available on Github.
Thanks for reading! I hope you find this article useful.
Node JS Event Loop And Custom Event
Node JS executes in a single process and single thread only, but the execution performance is very
high because of event-driven design and callback function usage. This article will introduce the
Node JS event loop mechanism and tell you how to create custom events in Node JS by example.
1. Node JS Event Loop Introduction
1. When you execute an asynchronous method provided by the Node JS framework, you should
provide a callback function to the method. You can continue to execute other js codes without
having to wait for the method to complete.
2. The callback function is an observer of the event queue. When the task is complete, the Node
JS framework will add the event back to the event queue, then the callback function which just
observes the event in the queue will be invoked to process the result.
3. An example of this design pattern is Node JS implemented HTTP web server. The HTTP web
server starts and listens for requests. When a request arrives, it will create an event in the queue
and then waiting for the next request.
4. When the previous request process is complete, the callback function will be invoked to send the
response back to the client.
5. This pattern is very high performance because web servers just waiting for requests and do not
do other things like IO operation which will cost time and resources.
2. Create And Listen Custom Event In Node JS
1. If you find Node JS built-in event is not enough for you, you can create a custom event and
bind the custom event process function with the event follow the below steps.
2. Include Node JS events built-in module.
var events = require('events');
3. Create an EventEmitter object use the above module
var event_emitter = new events.EventEmitter();
4. Create a JavaScript function as a callback function that will be triggered when the custom event
happen.
var data_receive_handler = function(){
console.log('Data received.');
}
5. Register custom events with the callback function. The callback function will observe the custom
event.
event_emitter.on('receive_data', data_receive_handler);
6. Trigger custom event then the callback function will be executed.
event_emitter.emit('receive_data');
3. Node JS Custom Event Example
1. custom-event.js
// Include node js prebuilt events module.
var events = require('events');
// Create event emitter object.
var event_emitter = new events.EventEmitter();
// Create connect event process function.
var connect_handler = function connected() {
console.log('Connect success.');
// Trigger receive_data event
event_emitter.emit('receive_data');
}
// Bind custom event connect with connect_handler process function.
event_emitter.on('connect', connect_handler);
// Create receive data event process function.
var data_receive_handler = function(){
console.log('Data received.');
}
// Bind custom event receive_data with data_receive_handler function.
event_emitter.on('receive_data', data_receive_handler);
// Trigger connect event.
event_emitter.emit('connect');
console.log("Code exit");
2. Run the below command to execute the above javascript source code in Node Js
$ node custom-event.js
3. Below is the above example output
Connect success.Data received.Code exit
So this was all in “Node JS Event Loop And Custom Event” ,I will be coming with more such
articles in the future.
Till then follow me for more !
2
Download