Learning Practices of NodeJS Basics
-
Base on JavaScript
-
Open Source Server Environment
-
Not a framework, it's a server
-
NodeJS is a JavaScript Runtime
In computer programming, a runtime system, also called runtime environment, primarily implements portions of an execution model.
-
Ryan Dahl: Original Developer of Node.js in 2009
-
Built on Chrome’s V8 Javascript Engine
-
[Chrome’s V8 JavaScript Engine] + [C++ Program] = Node.js
-
Node.js is a C++ Program which running with Chrome’s V8 JavaScript Engine
-
Include File System and Operating System etc, which are not included in Core JavaScript
-
Not multi-threaded. It runs in a single thread with a callback concept.
-
Behavior: Async, Non-Blocking
-
runs on various platforms (Windows, Linux, Unix, Mac OS X, etc.)
- Asynchronous and Event Driven
- Very Fast
- Single Threaded but Highly Scalable
- No Buffering
- I/O bound Applications
- Data Streaming Applications
- Data Intensive Real-time Applications (DIRT)
- JSON APIs based Applications
- Single Page Applications
- Not advisable to use Node.js for CPU intensive applications
- Asynchronous event driven IO helps concurrent request handling
- Uses JavaScript, which is easy to learn
- Share the same piece of code with both server and client side
- npm (Node Package Manager), the Node packaged modules has already become huge, and still growing
- Active and vibrant community, with lots of code shared via github, etc
- You can stream big files
- One CPU is not going to be enough; the platform provides no ability to scale out to take advantage of the multiple cores commonly present in today's server-class hardware.
- Dealing with relational databases is a pain if you are using Node.
- Every time using a callback ends up with tons of nested callbacks.
- Without diving in depth of JavaScript if someone starts Node, he may face conceptual problems.
- Node.js is not suited for CPU-intensive tasks. It is suited for I/O stuff only (like web servers).
- A callback function is called at the completion of a given task.
- An asynchronous equivalent for a function.
- Callbacks are nothing but functions that take some time to produce a result.
- Usually these async callbacks (async short for asynchronous) are used for accessing values from databases, downloading images, reading files etc.
- As these take time to finish, we can neither proceed to next line because it might throw an error saying unavailable nor we can pause our program.
- So we need to store the result and call back when it is complete.
// Callback Function
var callbackFunction = function(data) {
console.log('Task Status: '+ data);
};
// A Function in which Callback Function is used
var processAction = function(callback) {
callback('Success');
};
Use above function as:
processAction(callbackFunction);
- Also known as Pyramid of Doom
- An anti-pattern seen in code of asynchronous programming.
- It is a slang term used to describe and unwieldy number of nested “if” statements or functions.
- If you are not expecting your application logic to get too complex, a few callbacks seem harmless. But once your project requirements start to swell, you will quickly find yourself piling layers of nested callbacks. Congrats! Welcome to Callback Hell.
firstFunction(args, function() {
secondFunction(args, function() {
thirdFunction(args, function() {
// And so on…
});
});
});
See the pyramid shape and all the }) at the end? This is affectionately known as callback hell.
Not fixing it.
- Write comments
- Split functions into smaller functions
- Using Promises
- Using Async/await
const makeBurger = nextStep => {
getBeef(function (beef) {
cookBeef(beef, function (cookedBeef) {
getBuns(function (buns) {
putBeefBetweenBuns(buns, beef, function(burger) {
nextStep(burger)
})
})
})
})
}
// Make and serve the burger
makeBurger(function (burger) => {
serve(burger)
})
// Makes a burger
// makeBurger contains four steps:
// 1. Get beef
// 2. Cook the beef
// 3. Get buns for the burger
// 4. Put the cooked beef between the buns
// 5. Serve the burger (from the callback)
// We use callbacks here because each step is asynchronous.
// We have to wait for the helper to complete the one step
// before we can start the next step
const makeBurger = nextStep => {
getBeef(function(beef) {
cookBeef(beef, function(cookedBeef) {
getBuns(function(buns) {
putBeefBetweenBuns(buns, beef, function(burger) {
nextStep(burger);
});
});
});
});
};
Now, When you see the callback hell, you get an understanding of why it has to be written this way.
const getBeef = nextStep => {
const fridge = leftFright;
const beef = getBeefFromFridge(fridge);
nextStep(beef);
};
const cookBeef = (beef, nextStep) => {
const workInProgress = putBeefinOven(beef);
setTimeout(function() {
nextStep(workInProgress);
}, 1000 * 60 * 20);
};
const makeBurger = () => {
return getBeef()
.then(beef => cookBeef(beef))
.then(cookedBeef => getBuns(beef))
.then(bunsAndBeef => putBeefBetweenBuns(bunsAndBeef));
};
// Make and serve burger
makeBurger().then(burger => serve(burger));
const makeBurger = async () => {
const beef = await getBeef();
const cookedBeef = await cookBeef(beef);
const buns = await getBuns();
const burger = await putBeefBetweenBuns(cookedBeef, buns);
return burger;
};
// Make and serve burger
makeBurger().then(serve);
The event loop allows Node.js to perform non-blocking I/O operations despite the fact that JavaScript is single-threaded. It is done by assigning operations to the operating system whenever and wherever possible.
- An endless loop, which waits for tasks, executes them and then sleeps until it receives more tasks.
- Executes tasks from the event queue only when the call stack is empty i.e. there is no ongoing task.
- Allows us to use callbacks and promises.
- Executes the tasks starting from the oldest first.
console.log("This is the first statement");
setTimeout(function(){
console.log("This is the second statement");
}, 1000);
console.log("This is the third statement");
-
- Timers: Callbacks scheduled by setTimeout() or setInterval() are executed in this phase.
-
- Pending Callbacks: I/O callbacks deferred to the next loop iteration are executed here.
-
- Idle, Prepare: Used internally only.
-
- Poll: Retrieves new I/O events.
-
- Check: It invokes setIntermediate() callbacks.
-
- Close Callbacks: It handles some close callbacks. Eg: socket.on(‘close’, …)
Reference: https://www.educative.io/edpresso/what-is-the-event-loop-in-nodejs
- JavaScript has a concurrency model based on an event loop, which is responsible for executing the code, collecting and processing events, and executing queued sub-tasks.
- This model is quite different from models in other languages like C and Java.
- allows us to directly add a callback to the event queue.
- If you are familiar with the event loop, the check phase is specifically used to invoke callbacks set using the setImmediate method.
console.log('This is the first log');
setImmediate(() => console.log('This was queued!'))
console.log('This is the second log');
- Takes a callback and adds it to a queue.
- This callback is then added to the nextTickQueue so that it will be executed as soon as the current phase ends.
- The loop will block until the callback is resolved completely.
- Therefore, taking recursive process.nextTick calls can cause I/O starvation as the event loop might never reach that phase.
console.log('This is the first log');
process.nextTick(() => console.log('This was queued!'))
console.log('This is the second log');
- Both setImmediate and process.nextTick appear to be doing the same thing; however, you may prefer one over the other depending on your callback’s urgency.
- It is interesting to note that setImmediate adds callbacks to the event queue that are executed during the check phase, whereas process.nextTick executes callbacks immediately after the current phase.
Reference: https://developer.mozilla.org/en-US/docs/Web/JavaScript/EventLoop#runtime_concepts