Nodejs Term III - Callbacks
Nodejs Term III - Callbacks
Contents
Contents ......................................................................................................................................... 2
Methods .................................................................................................................................... 21
Properties ................................................................................................................................. 22
Example.................................................................................................................................... 23
Methods .................................................................................................................................... 24
Properties ................................................................................................................................. 25
Example.................................................................................................................................... 26
Flags .................................................................................................................................... 28
Syntax .................................................................................................................................. 30
Parameters .......................................................................................................................... 30
Example ............................................................................................................................... 31
Example #1 ........................................................................................................................... 33
Example #2 ........................................................................................................................... 34
Example #3 ........................................................................................................................... 34
1.1 Introduction
1.1.1 What is nodejs?
Node.js is an open-source server side runtime environment built on Chrome's V8 JavaScript engine. It
provides an event driven, non-blocking (asynchronous) I/O and cross-platform runtime environment for building
highly scalable server-side application using JavaScript.
Node.js can be used to build different types of applications such as command line application, web application,
real-time chat application, REST API server etc. However, it is mainly used to build network programs like web
servers, similar to PHP, Java, or ASP.NET.
Node.js was written and introduced by Ryan Dahl in 2009. Visit Wikipedia to know the history of Node.js.
Initially implemented as a client-side scripting language. Nowadays, it is used to execute JavaScript code and
scripts that run server-side to create dynamic web pages. The latest version of Node.js is 10.10.0.
Node.js is based on an event-driven architecture and a non-blocking Input/Output API that is designed to
optimize an application's throughput and scalability for real-time web applications.
Over a long period of time, the framework available for web development were all based on a stateless model.
A stateless model is where the data generated in one session (such as information about user settings and
events that occurred) is not maintained for usage in the next session with that user.
A lot of work had to be done to maintain the session information between requests for a user. But with Node.js
there is finally a way for web applications to have a real-time, two-way connections, where both the client and
server can initiate communication, allowing them to exchange data freely.
1.1.2 Why use Node.js?
1. Node.js is an open-source framework under MIT license. (MIT license is a free software license originating
at the Massachusetts Institute of Technology (MIT).)
2. Uses JavaScript to build entire server side application.
3. Lightweight framework that includes bare minimum modules. Other modules can be included as per the
need of an application.
4. Asynchronous by default. So it performs faster than other frameworks.
5. Cross-platform framework that runs on Windows, MAC or Linux
1.2 Processing model
In this section, we will learn about the Node.js process model and understand why we should use Node.js.
1.2.1 Traditional Web Server Model
In the traditional web server model, each request is handled by a dedicated thread from the thread pool. If no
thread is available in the thread pool at any point of time then the request waits till the next available thread.
Dedicated thread executes a particular request and does not return to thread pool until it completes the execution
and returns a response.
An event loop is constantly watching for the events to be raised for an asynchronous job and executing callback
function when the job completes. Internally, Node.js uses libev for the event loop which in turn uses internal C++
thread pool to provide asynchronous I/O.
The following figure illustrates asynchronous web server model using Node.js
Node.js process model increases the performance and scalability with a few caveats. Node.js is not fit for an
application which performs CPU-intensive operations like image processing or other heavy computation work
because it takes time to process a request and thereby blocks the single thread.
2. Node uses the V8 JavaScript Runtime engine, the one which is used by Google Chrome. Node
has a wrapper over the JavaScript engine which makes the runtime engine much faster and hence
processing of requests within Node also become faster.
3. Handling of concurrent requests – Another key functionality of Node is the ability to handle
concurrent connections with a very minimal overhead on a single process.
4. The Node.js library used JavaScript – This is another important aspect of development in
Node.js. A major part of the development community are already well versed in javascript, and hence,
development in Node.js becomes easier for a developer who knows javascript.
5. There are an Active and vibrant community for the Node.js framework. Because of the active
community, there are always keys updates made available to the framework. This helps to keep the
framework always up-to-date with the latest trends in web development.
Node.js is good when you need high levels of concurrency but less amount of dedicated CPU time.
Best of all, since Node.js is built on javascript, it's best suited when you build client-side applications which are
based on the same javascript framework.
In this section, you will learn about the tools required and steps to setup development environment to develop
a Node.js application.
Node.js development environment can be setup in Windows, Mac, Linux and Solaris. The following tools/SDK
are required for developing a Node.js application on any platform.
1. Node.js
NPM (Node Package Manager) is included in Node.js installation since Node version 0.6.0., so there is no need
to install it separately.
After you download the MSI, double-click on it to start the installation as shown below.
Click Next to read and accept the License Agreement and then click Install. It will install Node.js quickly on your
computer. Finally, click finish to complete the installation.
Once downloaded, click on the installer to start the Node.js installation wizard. Click on Continue and follow the
steps. After successful installation, it will display summary of installation about the location where it installed
Node.js and NPM.
After installation, verify the Node.js installation using terminal window and enter the following command. It will
display the version number of Node.js installed on your Mac.
$ node -v
Optionally, for Mac or Linux users, you can directly install Node.js from the command line using Homebrew
package manager for Mac OS or Linuxbrew package manager for Linux Operating System. For Linux, you will
need to install additional dependencies, viz. Ruby version 1.8.6 or higher and GCC version 4.2 or higher before
installing node.
1.7.4 IDE
Node.js application uses JavaScript to develop an application. So, you can use any IDE or texteditor tool that
supports JavaScript syntax. However, an IDE that supports auto complete features for Node.js API is
recommended e.g. Visual Studio, Sublime text, Eclipse, Aptana etc.
Node.js comes with virtual environment called REPL (aka Node shell). REPL stands for Read-Eval-Print-Loop.
It is a quick and easy way to test simple Node.js/JavaScript code.
To launch the REPL (Node shell), open command prompt (in Windows) or terminal (in Mac or UNIX/Linux) and
type node as shown below. It will change the prompt to > in Windows and MAC.
You can now test pretty much any Node.js/JavaScript expression in REPL. For example, if your write "10 + 20"
then it will display result 30 immediately in new line.
> 10 + 20
30
You can also define variables and perform some operation on them.
If you need to write multi line JavaScript expression or function then just press Enter whenever you want to write
something in the next line as a continuation of your code. The REPL terminal will display three dots (...), it means
you can continue on next line. Write .break to get out of continuity mode.
For example, you can define a function and execute it as shown below.
You can execute an external JavaScript file by writing node fileName command. For example, assume that node-
example.js is on C drive of your PC with following code.
node-example.js
console.log("Hello World");
Now, you can execute node-exampel.js from command prompt as shown below.
To exit from the REPL terminal, press Ctrl + C twice or write .exit and press Enter.
Thus, you can execute any Node.js/JavaScript code in the node shell (REPL). This will give you a result which
is similar to the one you will get in the console of Google Chrome browser.
Note: ECMAScript implementation in Node.js and browsers is slightly different. For example, {}+{} is '[object
Object][object Object]' in Node.js REPL, whereas the same code is NaN in the Chrome console because of the
automatic semicolon insertion feature. However, mostly Node.js REPL and the Chrome/Firefox consoles are
similar.
- String
- Number
- Boolean
- Undefined
- Null
- RegExp
- __dirname
- __filename
- Console
- Process
- Buffer
- setImmediate(callback[, arg][, ...])
- setInterval(callback, delay[, arg][, ...])
- setTimeout(callback, delay[, arg][, ...])
- clearImmediate(immediateObject)
- clearInterval(intervalObject)
- clearTimeout(timeoutObject)
All above objects are accessed via window or global objects. Window object is common in javascript but nodejs
uses global object instead.
Different from javascript, nodejs global object can not access custom objects, as a result none of the below
functions will print the message on the screen. SayHelloWindow() will not run because window object is not
known in nodejs and sayHelloGlobal will fail as well because global.message is not defined.
var message ="good morning"
function sayHelloWindow(){
console.log(window.message)
}
sayHello()
function sayHelloGlobal(){
console.log(global.message)
}
sayHelloGlobal()
1. Core Modules
2. Local Modules
In the above example of logging module, we have created an object with three functions - info(), warning() and
error(). At the end, we have assigned this object to module.exports. The module.exports in the above example
exposes a log object as a module.
The module.exports is a special object which is included in every JS file in the Node.js application by default.
Use module.exports or exports to expose a function, object or variable as a module in Node.js.
Now, let's see how to use the above logging module in our application.
Loading Local Module
To use local modules in your application, you need to load it using require() function in the same way as core
module. However, you need to specify the path of JavaScript file of the module.
The following example demonstrates how to use the above logging module contained in Log.js.
app.js
var myLogModule = require('./Log.js');
myLogModule.info('Node.js started');
In the above example, app.js is using log module. First, it loads the logging module using require() function and
specified path where logging module is stored. Logging module is contained in Log.js file in the root folder. So,
we have specified the path './Log.js' in the require() function. The '.' denotes a root folder.
The require() function returns a log object because logging module exposes an object in Log.js using module.ex-
ports. So now you can use logging module as an object and call any of its function using dot notation e.g myLog-
Module.info() or myLogModule.warning() or myLogModule.error()
Run the above example using command prompt (in Windows) as shown below.
C:\> node app.js
Info: Node.js started
Thus, you can create a local module using module.exports and use it in your application.
Export Module in Node.js
In the previous section, you learned how to write a local module using module.exports. In this section, you will
learn how to expose different types as a module using module.exports.
The module.exports or exports is a special object which is included in every JS file in the Node.js application
by default. module is a variable that represents current module and exports is an object that will be exposed as
a module. So, whatever you assign to module.exports or exports, will be exposed as a module.
Let's see how to expose different types as a module using module.exports.
Export Literals
As mentioned above, exports is an object. So it exposes whatever you assigned to it as a module. For example,
if you assign a string literal then it will expose that string literal as a module.
The following example exposes simple string message as a module in Message.js.
Message.js
module.exports = 'Hello world';
//or
exports = 'Hello world';
Now, import this message module and use it as shown below.
app.js
var msg = require('./Messages.js');
console.log(msg);
Run the above example and see the result as shown below.
C:\> node app.js
Hello World
Note: You must specify './' as a path of root folder to import a local module. However, you do not need to specify
path to import Node.js core module or NPM module in the require() function.
Export Object
exports is an object. So, you can attach properties or methods to it. The following example exposes an object
with a string property in Message.js file.
Message.js
exports.SimpleMessage = 'Hello world';
//or
module.exports.SimpleMessage = 'Hello world';
In the above example, we have attached a property "SimpleMessage" to the exports object. Now, import and
use this module as shown below.
app.js
var msg = require('./Messages.js');
console.log(msg.SimpleMessage);
In the above example, require() function will return an object { SimpleMessage : 'Hello World'} and assign it to
the msg variable. So, now you can use msg.SimpleMessage.
Run the above example by writing node app.js in the command prompt and see the output as shown below.
C:\> node app.js
Hello World
The same way as above, you can expose an object with function. The following example exposes an object with
log function as a module.
Log.js
module.exports.log = function (msg) {
console.log(msg);
};
The above module will expose an object- { log : function(msg){ console.log(msg); } } . Use the above module as
shown below.
app.js
var msg = require('./Log.js');
msg.log('Hello World');
Run and see the output in command prompt as shown below.
C:\> node app.js
Hello World
You can also attach an object to module.exports as shown below.
data.js
module.exports = {
firstName: 'James',
lastName: 'Bond'
}
app.js
var person = require('./data.js');
console.log(person.firstName + ' ' + person.lastName);
Run the above example and see the result as shown below.
C:\> node app.js
James Bond
Export Function
You can attach an anonymous function to exports object as shown below.
Log.js
module.exports = function (msg) {
console.log(msg);
};
Now, you can use the above module as below.
app.js
var msg = require('./Log.js');
msg('Hello World');
The msg variable becomes function expression in the above example. So, you can invoke the function using
parenthesis (). Run the above example and see the output as shown below.
C:\> node app.js
Hello World
Export function as a class
In the JavaScript, a function can be treated like a class. The following example exposes a function which can be
used like a class.
Person.js
module.exports = function (firstName, lastName) {
this.firstName = firstName;
this.lastName = lastName;
this.fullName = function () {
return this.firstName + ' ' + this.lastName;
}
}
As you can see, we have created a person object using new keyword. Run the above example as below.
C:\> node app.js
James Bond
In this way, you can export and import a local module created in a separate file under root folder.
Node.js also allows you to create modules in sub folders. Let's see how to load module from sub folders.
Load Module from Separate Folder
Use the full path of a module file where you have exported it using module.exports. For example, if log module
in the log.js is stored under "utility" folder under the root folder of your application then import it as shown below.
app.js
var log = require('./utility/log.js');
In the above example, . is for root folder and then specify exact path of your module file. Node.js also allows us
to specify the path to the folder without specifying file name. For example, you can specify only utility folder
without specifing log.js as shown below.
app.js
var log = require('./utility');
In the above example, Node will search for a package definition file called package.json inside utility folder. This
is because Node assumes that this folder is a package and will try to look for a package definition. The pack-
age.json file should be in a module directory. The package.json under utility folder specifies the file name using
"main" key as below.
./utility/package.json
{
"name" : "log",
"main" : "./log.js"
}
Now, Node.js will find log.js file using main entry in package.json and import it.
Note:
If package.json file does not exist then it will look for index.js file as a module file by default.
Methods
1
path.normalize(p)
2
path.join([path1][, path2][, ...])
Join all the arguments together and normalize the resulting path.
3
path.resolve([from ...], to)
4
path.isAbsolute(path)
Determines whether the path is an absolute path. An absolute path will always resolve
to the same location, regardless of the working directory.
5
path.relative(from, to)
6
path.dirname(p)
Return the directory name of a path. Similar to the Unix dirname command.
7
path.basename(p[, ext])
Return the last portion of a path. Similar to the Unix basename command.
8
path.extname(p)
Return the extension of the path, from the last '.' to end of string in the last portion of
the path. If there is no '.' in the last portion of the path or the first character of it is '.',
then it returns an empty string.
9
path.parse(pathString)
10
path.format(pathObject)
Properties
1
path.sep
2
path.delimiter
3
path.posix
Provide access to aforementioned path methods but always interact in a posix com-
patible way.
4
path.win32
Provide access to aforementioned path methods but always interact in a win32 com-
patible way.
Example
// Normalization
console.log('normalization : ' + path.normalize('/test/test1//2slashes/1slash/tab/..'));
// Join
console.log('joint path : ' + path.join('/test', 'test1', '2slashes/1slash', 'tab', '..'));
// Resolve
console.log('resolve : ' + path.resolve('main.js'));
// extName
console.log('ext name : ' + path.extname('main.js'));
$ node main.js
normalization : /test/test1/2slashes/1slash
joint path : /test/test1/2slashes/1slash
resolve : /web/com/1427176256_27423/main.js
ext name : .js
2.4.2 OS module
Node.js os module provides a few basic operating-system related utility functions. This module can be imported
using the following syntax.
var os = require("os")
Methods
1
os.tmpdir()
Returns the endianness of the CPU. Possible values are "BE" or "LE".
3
os.hostname()
4
os.type()
5
os.platform()
6
os.arch()
Returns the operating system CPU architecture. Possible values are "x64",
"arm" and "ia32".
7
os.release()
8
os.uptime()
9
os.loadavg()
10
os.totalmem()
11
os.freemem()
Returns the amount of free system memory in bytes.
12
os.cpus()
13
os.networkInterfaces()
Properties
1
os.EOL
A constant defining the appropriate End-of-line marker for the operating sys-
tem.
Example
The following example demonstrates a few OS methods. Create a js file named main.js with the following code.
var os = require("os");
// Endianness
console.log('endianness : ' + os.endianness());
// OS type
console.log('type : ' + os.type());
// OS platform
console.log('platform : ' + os.platform());
$ node main.js
endianness : LE
type : Linux
platform : linux
total memory : 25103400960 bytes.
free memory : 20676710400 bytes.
options: The options parameter can be an object or string which can include encoding and flag. The
default encoding is utf8 and default flag is "r".
callback: A function with two parameters err and fd. This will get called when readFile operation com-
pletes.
console.log(data);
});
The above example reads TestFile.txt (on Windows) asynchronously and executes callback function when read
operation completes. This read operation either throws an error or completes successfully. The err parameter
contains error information if any. The data parameter contains the content of the specified file.
The following is a sample TextFile.txt file.
TextFile.txt
This is test file to test fs module of Node.js
Now, run the above example and see the result as shown below.
C:\> node server.js
This is test file to test fs module of Node.js
Use fs.readFileSync() method to read file synchronously as shown below.
Example: Reading File Synchronously
var fs = require('fs');
options: The options parameter can be an object or string which can include encoding, mode and flag.
The default encoding is utf8 and default flag is "r".
callback: A function with two parameters err and fd. This will get called when write operation completes.
The following example creates a new file called test.txt and writes "Hello World" into it asynchronously.
Example: Creating & Writing File
var fs = require('fs');
Mode: The mode for read, write or readwrite. Defaults to 0666 readwrite.
callback: A function with two parameters err and fd. This will get called when file open operation com-
pletes.
Flags
The following table lists all the flags which can be used in read/write operation.
Flag Description
r Open file for reading. An exception occurs if the file does not exist.
r+ Open file for reading and writing. An exception occurs if the file does not exist.
rs Open file for reading in synchronous mode.
rs+ Open file for reading and writing, telling the OS to open it synchronously. See notes for 'rs' about using
this with caution.
w Open file for writing. The file is created (if it does not exist) or truncated (if it exists).
wx Like 'w' but fails if path exists.
w+ Open file for reading and writing. The file is created (if it does not exist) or truncated (if it exists).
wx+ Like 'w+' but fails if path exists.
a Open file for appending. The file is created if it does not exist.
ax Like 'a' but fails if path exists.
a+ Open file for reading and appending. The file is created if it does not exist.
ax+ Like 'a+' but fails if path exists.
The following example opens an existing file and reads its content.
Example:File open and read
var fs = require('fs');
if (err) {
return console.error(err);
}
Delete File
Use fs.unlink() method to delete an existing file.
Signature:
fs.unlink(path, callback);
The following example deletes an existing file.
Example:File Open and Read
var fs = require('fs');
fs.unlink('test.txt', function () {
});
Syntax
Following is the syntax of the method to get the information about a file −
fs.stat(path, callback)
Parameters
callback − This is the callback function which gets two arguments (err, stats) where stats is an
object of fs.Stats type which is printed below in the example.
Apart from the important attributes which are printed below in the example, there are several useful methods
available in fs.Stats class which can be used to check file type. These methods are given in the following table.
1
stats.isFile()
2
stats.isDirectory()
4
stats.isCharacterDevice()
5
stats.isSymbolicLink()
6
stats.isFIFO()
7
stats.isSocket()
Example
var fs = require("fs");
Example #1
emitter.on('newEvent', function(user){
console.log(user);
});
emitter.emit('newEvent', "Krunal");
In above example, first, we are going to import events object, and then we get emitter object.
1. On
2. emit
So, if we make an object of EventEmitter class, then we have access to this methods.
emitter.on('newEvent', function(user){
console.log(user);
});
Here, I have defined an event and till now, not called just determine.
On() method takes an event name and a call back function, which describes the logic and payload of the function.
As we know, Node.js has the event-driven architecture, so it first occurs the events and then related to that event,
one callback function is returned EMIT.
emitter.emit('newEvent', "Krunal");
Here, I have emitted the event, so related to that event, the callback function is invoked, and that function will
execute. The first parameter is event name and second is payload.
Krunal
Example #2
this.username = username;
util.inherits(User, EventEmitter);
user.on('nuevent', function(props){
console.log(props);
});
user.emit('nuevent', 'dancing');
In this example, first I have created an EventEmitter object and then also create User function constructor.
Then, import the Node.js’s core module util and inherits the base functionality from EventEmitter module to the
newly created User module.
So, now User has all the methods and properties of the EventEmitter module, and we can use two methods on
it.
Now, user object’s behavior is same as EventEmitter, and we can define events on it and emit the events.
Example #3
});
myEmitter.emit('event');
First, we have included the EventEmitter class, and then we define our class and extends the base EventEmit-
ter class.
Now, make an object of the newly created class, so we have access all the methods of the EventEmitterclass.
Rest is all the same.
In this section, we will learn how to create a simple Node.js web server and handle HTTP requests.
To access web pages of any web application, you need a web server. The web server will handle all the http
requests for the web application e.g IIS is a web server for ASP.NET web applications and Apache is a web
server for PHP or Java web applications.
Node.js provides capabilities to create your own web server which will handle HTTP requests asynchronously.
You can use IIS or Apache to run Node.js web application but it is recommended to use Node.js web server.
Create Node.js Web Server
Node.js makes it easy to create a simple web server that processes incoming requests asynchronously.
The following example is a simple Node.js web server contained in server.js file.
server.js
var http = require('http'); // 1 - Import Node.js core module
var server = http.createServer(function (req, res) { // 2 - creating server
});
server.listen(5000); //3 - listen for any incoming requests
console.log('Node.js web server at port 5000 is running..')
In the above example, we import the http module using require() function. The http module is a core module of
Node.js, so no need to install it using NPM. The next step is to call createServer() method of http and specify
callback function with request and response parameter. Finally, call listen() method of server object which was
returned from createServer() method with port number, to start listening to incoming requests on port 5000. You
can specify any unused port here.
Run the above web server by writing node server.js command in command prompt or terminal window and it will
display message as shown below.
C:\> node server.js
Node.js web server at port 5000 is running..
This is how you create a Node.js web server using simple steps. Now, let's see how to handle HTTP request
and send response in Node.js web server.
Handle HTTP Request
The http.createServer() method includes request and response parameters which is supplied by Node.js. The
request object can be used to get information about the current HTTP request e.g., url, request header, and
data. The response object can be used to send a response for a current HTTP request.
The following example demonstrates handling HTTP request and response in Node.js.
server.js
var http = require('http'); // Import Node.js core module
}
else if (req.url == "/student") {
}
else if (req.url == "/admin") {
});
For Windows users, point your browser to http://localhost:5000 and see the following result.
server.listen(5000);
Parse an address with the url.parse() method, and it will return a URL object with each part of the address as
properties:
Example
Split a web address into readable parts:
summer.html
<!DOCTYPE html>
<html>
<body>
<h1>Summer</h1>
</body>
</html>
winter.html
<!DOCTYPE html>
<html>
<body>
<h1>Winter</h1>
</body>
</html>
Create a Node.js file that opens the requested file and returns the content to the client. If anything goes wrong,
throw a 404 error:
demo_fileserver.js
var fs = require('fs');
if (err) {
res.write(data);
return res.end();
});
}).listen(8080);
NPM is written entirely in JavaScript(JS) and was developed by Isaac Z. Schlueter with inspiration from the
shortcomings of other similar projects such as PHP) and CPAN (Perl).
If you have installed Node.js in your machine then by default NPM has already been installed. The-
Node.js comes with NPM.
You can check the version of your NPM using the following command.
All of your dependencies are written in one file inside your project root called package.json. You can create
the package.json file using the following command below.
npm init
If you have already the package.json file, but dependencies are not installed previously then you can install all
the dependencies defined on your package.json file using the following command.
npm install
If you want to install development dependency, then you can hit the following command with the –dev flag.
# or
npm install -D <package-name>
The difference between dependency and devDependencies are usually development tools, like a testing li-
brary, while dependencies are bundled with the app in production.
If you do not want to write install, then you can use i, and it will do the work for you.
npm i <package-name>
If you want to save the dependency inside the package.json file, then you need to add the –save flag.
NPM will generate one folder called node_modules when you install any library from the Node Package Man-
ager Repository.
npm update
You can also update the specific package using the following command.
If you are running an old version of NPM, then you can update it to the latest release by the following command
from root.
If you specify the exact version of the NPM libraries, then it also helps to keep everyone on the same version of
a library, so that the whole team runs the same version and no conflicts occur until the package.json file is
updated.
If you are using the Git version control system, then you need to upload the package.json file and not a
node_modules folder. So when another developer downloads the project, it has already the package.json file,
and he only needs to hit the npm install command to up and running with the project.
If we do not specify any version, then it will install the latest version of the particular package.
Semantic Versioning
If there’s one great thing in Node.js packages, is that all agreed on using Semantic Versioning for their version
numbering.The Semantic Versioning concept is simple: all versions have 3 digits: x.y.z.
When you make a new release, you don’t just up a number as you please, but you have rules:
you up the major version when you make incompatible API changes
you up the minor version when you add functionality in a backward-compatible manner
you up the patch version when you make backward-compatible bug fixes
The convention is adopted all across programming languages, and it is very important that every npm package
adheres to it, because the whole system depends on that.
^
~
>
>=
<
<=
=
-
||
^: if you write ^0.13.0 when running npm update it can update to patch and minor releases: 0.13.1, 0.14.0
and so on.
~: if you write ~0.13.0, when running npm update it can update to patch releases: 0.13.1 is ok, but 0.14.0
is not.
>: you accept any version higher than the one you specify
>=: you accept any version equal to or higher than the one you specify
<=: you accept any version equal or lower to the one you specify
<: you accept any version lower to the one you specify
You can combine some of those notations, for example use 1.0.0 || >=1.1.0 <1.2.0 to either use 1.0.0 or one
release from 1.1.0 up, but lower than 1.2.0.
no symbol: you accept only that specific version you specify (1.2.1)
Globally installed packages are stored in the system directory. Such dependencies can be used in CLI (Com-
mand Line Interface) function of any node.js but cannot be imported using require() in Node application directly.
Now let’s try installing the express module using global installation.
It will produce a similar result, but the module will be installed globally.
Uninstalling a Module
Search a Module
Running Tasks
The package.json file supports a format for specifying command line tasks that can be run by using
npm <task-name>
For example:
{
"scripts": {
},
"scripts": {
},
So instead of typing those extended commands, which are easy to forget or mistype, you can run
$ npm watch
$ npm dev
Using npm we can publish our our modules, follow this steps to publish you module:
Step4: run npm publish – Append some numbers to you module name if you get an error
Step5: Create another project and run npm install your-module and use it in your new project
Later if you make changes and want to publish update version run npm version {major or minor or patch}
depending on the changes made, then run npm publish
2.5.6 Version Control
Documentation
Being able to go back through the history of a project to see the decisions that were made and the order in
which components were developed can be valuable documentation. Having a technical history of your project
can be quite useful.
Attribution
If you work on a team, attribution can be hugely important. Whenever you find something in code that is
opaque or questionable, knowing who made that change can save you many hours. It could be that the
comments associated with the change are sufficient to answer your questions, and if not, you’ll know who to
talk to.
Experimentation
A good version control system enables experimentation. You can go off on a tangent, trying something new,
without fear of affecting the stability of your project. If the experiment is successful, you can fold it back into the
project, and if it is not successful, you can abandon it.
We will be using Git, but you are welcome to substitute it with any other version control of your choice, e.g svn,
mercurial,...
Assume you have a project folder with your initial work. You can place it under Git revision control as follows.
$ mkdir project
$ cd project
$ git init
You’ve now initialized the working directory—you may notice a new directory created, named ".git".
Next, tell Git to take a snapshot of the contents of all files under the current directory (note the .), with git add:
$ git add .
This snapshot is now stored in a temporary staging area which Git calls the "index". You can permanently store
the contents of the index in the repository with git commit:
$ git commit
This will prompt you for a commit message. You’ve now stored the first version of your project in Git.
Making changes
Modify some files, then add their updated contents to the index:
You are now ready to commit. You can see what is about to be committed using git diff with the --cached op-
tion:
(Without --cached, git diff will show you any changes that you’ve made but not yet added to the index.) You
can also get a brief summary of the situation with git status:
$ git status
On branch master
Changes to be committed:
modified: file1
modified: file2
modified: file3
If you need to make any further adjustments, do so now, and then add any newly modified content to the index.
Finally, commit your changes with:
$ git commit
This will again prompt you for a message describing the change, and then record a new version of the project.
$ git commit -a
which will automatically notice any modified (but not new) files, add them to the index, and commit, all in one
step.
A note on commit messages: Though not required, it’s a good idea to begin the commit message with a single
short (less than 50 character) line summarizing the change, followed by a blank line and then a more thorough
description. The text up to the first blank line in a commit message is treated as the commit title, and that title is
used throughout Git.
$ git log
$ git log -p
Often the overview of the change is useful to get a feel of each step
Managing branches
A single Git repository can maintain multiple branches of development. To create a new branch named "experi-
mental", use
$ git branch
experimental
* master
The "experimental" branch is the one you just created, and the "master" branch is a default branch that was
created for you automatically. The asterisk marks the branch you are currently on; type
to switch to the experimental branch. Now edit a file, commit the change, and switch back to the master
branch:
(edit file)
$ git commit -a
Check that the change you made is no longer visible, since it was made on the experimental branch and you’re
back on the master branch.
(edit file)
$ git commit -a
at this point the two branches have diverged, with different changes made in each. To merge the changes
made in experimental into master, run
If the changes don’t conflict, you’re done. If there are conflicts, markers will be left in the problematic files
showing the conflict;
$ git diff
will show this. Once you’ve edited the files to resolve the conflicts,
$ git commit -a
$ gitk
This command ensures that the changes in the experimental branch are already in the current branch.
If you develop on a branch crazy-idea, then regret it, you can always delete the branch with
Branches are cheap and easy, so this is a good way to try something out.
This creates a new directory "myrepo" containing a clone of Alice’s repository. The clone is on an equal footing
with the original project, possessing its own copy of the original project’s history.
(edit files)
When he’s ready, he tells Alice to pull changes from the repository at /home/bob/myrepo. She does this with:
alice$ cd /home/alice/project
This merges the changes from Bob’s "master" branch into Alice’s current branch. If Alice has made her own
changes in the meantime, then she may need to manually fix any conflicts.
The "pull" command thus performs two operations: it fetches changes from a remote branch, then merges
them into the current branch.
Note that in general, Alice would want her local changes committed before initiating this "pull". If Bob’s work
conflicts with what Alice did since their histories forked, Alice will use her working tree and the index to resolve
conflicts, and existing local changes will interfere with the conflict resolution process (Git will still perform the
fetch but will refuse to merge --- Alice will have to get rid of her local changes in some way and pull again when
this happens).
Alice can peek at what Bob did without merging first, using the "fetch" command; this allows Alice to inspect
what Bob did, using a special symbol "FETCH_HEAD", in order to determine if he has anything worth pulling,
like this:
This operation is safe even if Alice has uncommitted local changes. The range notation
"HEAD..FETCH_HEAD" means "show everything that is reachable from the FETCH_HEAD but exclude any-
thing that is reachable from HEAD". Alice already knows everything that leads to her current state (HEAD), and
reviews what Bob has in his state (FETCH_HEAD) that she has not seen with this command.
If Alice wants to visualize what Bob did since their histories forked she can issue the following command:
$ gitk HEAD..FETCH_HEAD
This uses the same two-dot range notation we saw earlier with git log.
Alice may want to view what both of them did since they forked. She can use three-dot form instead of the two-
dot form:
$ gitk HEAD...FETCH_HEAD
This means "show everything that is reachable from either one, but exclude anything that is reachable from
both of them".
Please note that these range notation can be used with both gitk and "git log".
After inspecting what Bob did, if there is nothing urgent, Alice may decide to continue working without pulling
from Bob. If Bob’s history does have something Alice would immediately need, Alice may choose to stash her
work-in-progress first, do a "pull", and then finally unstash her work-in-progress on top of the resulting history.
When you are working in a small closely knit group, it is not unusual to interact with the same repository over
and over again. By defining remote repository shorthand, you can make it easier:
With this, Alice can perform the first part of the "pull" operation alone using the git fetch command without
merging them with her own branch, using:
Unlike the longhand form, when Alice fetches from Bob using a remote repository shorthand set up with git
remote, what was fetched is stored in a remote-tracking branch, in this case bob/master. So after this:
shows a list of all the changes that Bob made since he branched from Alice’s master branch.
After examining those changes, Alice could merge the changes into her master branch:
This merge can also be done by pulling from her own remote-tracking branch, like this:
Note that git pull always merges into the current branch, regardless of what else is given on the command line.
Later, Bob can update his repo with Alice’s latest changes using
bob$ git pull
Note that he doesn’t need to give the path to Alice’s repository; when Bob cloned Alice’s repository, Git stored
the location of her repository in the repository configuration, and that location is used for pulls:
/home/alice/project
(The complete configuration created by git clone is visible using git config -l, and the git-config[1] man page
explains the meaning of each option.)
Git also keeps a pristine copy of Alice’s master branch under the name "origin/master":
origin/master
If Bob later decides to work from a different host, he can still perform clones and pulls using the ssh protocol:
Exploring history
Git history is represented as a series of interrelated commits. We have already seen that the git log command
can list those commits. Note that first line of each git log entry also gives a name for the commit:
$ git log
commit c82a22c39cbc32576f64f5c6b3f24b99ea8149c7
We can give this name to git show to see the details about this commit.
$ git show c82a22c39cbc32576f64f5c6b3f24b99ea8149c7
But there are other ways to refer to commits. You can use any initial part of the name that is long enough to
uniquely identify the commit:
$ git show c82a22c39c # the first few characters of the name are
# usually enough
Every commit usually has one "parent" commit which points to the previous state of the project:
Note that merge commits may have more than one parent:
$ git show HEAD^1 # show the first parent of HEAD (same as HEAD^)
You can also give commits names of your own; after running
you can refer to 1b2e1d63ff by the name "v2.5". If you intend to share this name with other people (for exam-
ple, to identify a release version), you should create a "tag" object, and perhaps sign it; see git-tag[1]for details.
Any Git command that needs to know a commit can take any of these names. For example:
$ git branch stable v2.5 # start a new branch named "stable" based
# at v2.5
$ git reset --hard HEAD^ # reset your current branch and working
Be careful with that last command: in addition to losing any changes in the working directory, it will also remove
all later commits from this branch. If this branch is the only branch containing those commits, they will be lost.
Also, don’t use git reset on a publicly-visible branch that other developers pull from, as it will force needless
merges on other developers to clean up the history. If you need to undo changes that you have pushed,
use git revert instead.
The git grep command can search for strings in any version of your project, so
If you leave out the commit name, git grep will search any of the files it manages in your current directory. So
is a quick way to search just the files that are tracked by Git.
Many Git commands also take sets of commits, which can be specified in a number of ways. Here are some
examples with git log:
$ git log --since="2 weeks ago" # commits from the last 2 weeks
# Makefile
You can also give git log a "range" of commits where the first is not necessarily an ancestor of the second; for
example, if the tips of the branches "stable" and "master" diverged from a common commit some time ago,
then
will show the list of commits made on the stable branch but not the master branch.
The git log command has a weakness: it must present commits in a list. When the history has lines of devel-
opment that diverged and then merged back together, the order in which git log presents those commits is
meaningless.
Most projects with multiple contributors (such as the Linux kernel, or Git itself) have frequent merges,
and gitk does a better job of visualizing their history. For example,
allows you to browse any commits from the last 2 weeks of commits that modified files under the "drivers" di-
rectory. (Note: you can adjust gitk’s fonts by holding down the control key while pressing "-" or "+".)
Finally, most commands that take filenames will optionally allow you to precede any filename by a commit, to
specify a particular version of the file:
You can also use git show to see any such file:
Fig: Express.js
4. Includes various middleware modules which you can use to perform additional tasks on request and
response.
5. Easy to integrate with different template engines like Jade, Vash, EJS etc.
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta charset="utf-8" />
<title></title>
</head>
<body>
<form action="/submit-student-data" method="post">
First Name: <input name="firstName" type="text" /> <br />
Last Name: <input name="lastName" type="text" /> <br />
<input type="submit" />
</form>
</body>
</html>
Body Parser
To handle HTTP POST request in Express.js version 4 and above, you need to install middleware module
called body-parser. The middleware was a part of Express.js earlier but now you have to install it separately.
This body-parser module parses the JSON, buffer, string and url encoded data submitted using HTTP POST
request. Install body-parser using NPM as shown below.
npm install body-parser --save
Now, import body-parser and get the POST request data as shown below.
app.js: Handle POST Route in Express.js
var express = require('express');
var app = express();
//setting middleware
app.use(express.static(__dirname + 'public')); //Serves resources from public folder
var server = app.listen(5000);
Note: Specify absolute path in express.static() by prepending __dirname. This will not break your application
even if you run the express app from another directory.
In the above example, app.use() method mounts the middleware express.static for every request. The ex-
press.static middleware is responsible for serving the static assets of an Express.js application. The ex-
press.static() method specifies the folder from which to serve all static resources.
Now, run the above code using node server.js command and point your browser to http://lo-
calhost:5000/myImage.jpg and it will display myImage.jpg from the public folder (public folder should have
myImage.jpg).
If you have different folders for different types of resources then you can set express.static middleware as shown
below.
Example: Serve resources from different folders
var express = require('express');
var app = express();
app.use(express.static('public'));
//Serves all the request which includes /images in the url from Images folder
app.use('/images', express.static(__dirname + '/Images'));
fileServer.serve(req, res);
}).listen(5000);
In the above example, node-static will serve static files from public folder by default. So, an URL request will
automatically map to the file in the public folder and will send it as a response.
Now, run the above example using node server.js command and point your browser to http://lo-
calhost:5000/myImage.jpg (assuming that public folder includes myImage.jpg file) and it will display the image
on your browser. You don't need to give "/public/myImage.jpg" because it will automatically serve all the static
files from the public folder.
3.7 Response and Request objects
3.7.1 Introduction
When you’re building a web server with Express, most of what you’ll be doing starts with a request object and
ends with a response object. These two objects originate in Node and are extended by Express. Before we delve
into what these objects offer us, let’s establish a little background on how a client (a browser, usually) requests
a page from a server, and how that page is returned.
The Parts of a URL
Protocol
The protocol determines how the request will be transmitted. We will be dealing
exclusively with http and https. Other common protocols include file and ftp.
Host
The host identifies the server. Servers on your computer (localhost) or a local network may simply be one word,
or it may be a numeric IP address. On the Internet,the host will end in a top-level domain (TLD) like .com or .net.
Additionally, there may be subdomains, which prefix the hostname. www is a very common subdomain,though
it can be anything. Subdomains are optional.
Port
Each server has a collection of numbered ports. Some port numbers are “special,” like 80 and 443. If you omit
the port, port 80 is assumed for HTTP and 443 for HTTPS. In general, if you aren’t using port 80 or 443, you
should use a port number greater than 1023.1 It’s very common to use easy-to-remember port numbers like
3000, 8080, and 8088.
Path
The path is generally the first part of the URL that your app cares about (it is possible to make decisions based
on protocol, host, and port, but it’s not good practice). The path should be used to uniquely identify pages or
other resources in your app.
Querystring
Fragment
The fragment (or hash) is not passed to the server at all: it is strictly for use by the browser. It is becoming
increasingly common for single-page applications or AJAXheavy applications to use the fragment to control the
application. Originally, the fragment’s sole purpose was to cause the browser to display a specific part of the
document, marked by an anchor tag (<a id="chapter06">)
req.params
An array containing the named route parameters. We’ll learn more about this in next lectures.
req.param(name)
req.query
An object containing querystring parameters (sometimes called GET parameters) as name/value pairs. They are
used to hold optional parameters
req.body
An object containing POST parameters. It is so named because POST parameters are passed in the body of the
REQUEST, not in the URL like querystring parameters. To make req.body available, you’ll need middleware that
can parse the body content type, like body-parse as we saw previously.
req.route
Information about the currently matched route. Primarily useful for route debugging.
req.cookies/req.signedCookies
req.headers
A convenience method to determine whether the client accepts a given type or types (optional types can be a
single MIME type, such as application/json, a commadelimited list, or an array). This method is of primary interest
to those writing public APIs; it is assumed that browsers will always accept HTML by default.
req.ip
req.path
req.host
A convenience method that returns the hostname reported by the client. This information can be spoofed and
should not be used for security purposes. req.xhr A convenience property that returns true if the request
originated from an AJAX call.
req.protocol
The protocol used in making this request (for our purposes, it will either be http or https).
req.secure
A convenience property that returns true if the connection is secure. Equivalent to req.protocol==='https'.
req.url/req.originalUrl
A bit of a misnomer, these properties return the path and querystring (they do not include protocol, host, or port).
req.url can be rewritten for internal routing purposes, but req.originalUrl is designed to remain the original request
and querystring.
req.acceptedLanguages
A convenience method that returns an array of the (human) languages the client prefers, in order. This
information is parsed from the request header.
res.status(code)
Sets the HTTP status code. Express defaults to 200 (OK), so you will have to use this method to return a status
of 404 (Not Found) or 500 (Server Error), or any other status code you wish to use. For redirects (status codes
301, 302, 303, and 307), there is a method redirect, which is preferable.
res.set(name, value)
Sets a response header. This is not something you will normally be doing manually.
Sets or clears cookies that will be stored on the client. This requires some middleware support.
res.redirect([status], url)
Redirects the browser. The default redirect code is 302 (Found). In general, you should minimize redirection
unless you are permanently moving a page, in which case you should use the code 301 (Moved Permanently).
Sends a response to the client, with an optional status code. Express defaults to a content type of text/html, so
if you want to change it to text/plain (for example), you’ll have to call res.set('Content-Type', 'text/plain\') before
calling res.send. If body is an object or an array, the response is sent as JSON instead (with the content type
being set appropriately), though if you want to send JSON, I recommend doing so explicitly by calling res.json
instead.
res.type(type)
A convenience method to set the Content-Type header. Essentially equivalent to res.set('Content-Type', type),
except that it will also attempt to map file extensions to an Internet media type if you provide a string without a
slash in it. For example, res.type('txt') will result in a Content-Type of text/plain. There are areas where this
functionality could be useful (for example, automatically serving disparate multimedia files), but in general, you
should avoid it in favor of explicitly setting the correct Internet media type.
res.format(object)
This method allows you to send different content depending on the Accept request header. This is of primary
use in APIs, and we will discuss this more in coming chapters. Here’s a very simple example:
res.format({'text/plain': 'hi there','text/html': '<b>hi there</b>'}).
Both of these methods set a response header called Content-Disposition to attachment; this will prompt the
browser to download the content instead of displaying it in a browser. You may specify filename as a hint to the
browser. With res.download, you can specify the file to download, whereas res.attachment just sets the header;
you still have to send content to the client.
res.sendFile(path, [options], [callback])
This method will read a file specified by path and send its contents to the client. There should be little need for
this method; it’s easier to use the static middleware,and put files you want available to the client in the public
directory. However, if you want to have a different resource served from the same URL depending on some
condition, this method could come in handy.
res.links(links) Sets the Links response header. This is a specialized header that has little use in most
applications.
res.locals is an object containing default context for rendering views. res.render will render a view using the
configured templating engine.
EXAMPLES
Write All necessary CRUD APIs for course management application where each course has these fields:
id(number), name(string), authorId(number) and price(number), the course depends on author object
which has fields: id(number), names(string), email(string)
- Course name must be string of minimum length 3 and always required for course to be either saved
or updated
Write back end CRUD APIs for shop management application where two objects are Products( has fields:
id, name, description, categoryId) and categories(id, name)
Note:
o Validation rules
- Course name must be string of minimum length 3 and always required for course to be either saved
or updated
4.1.0 Introduction
Middleware function is a function that has access to the request object (req), the response object (res), and the
next middleware function in the application’s request-response cycle. The next middleware function is commonly
denoted by a variable named next.
Express.js Middleware are also known as different types of functions that are invoked by the Express.js routing
layer before the final request handler. As the name specified, Middleware appears in the middle between an
initial request and final intended route. In stack, middleware functions are always invoked in the order in which
they are added.
If the current middleware function does not end the request-response cycle, it must call next() to pass control to
the next middleware function. Otherwise, the request will be left hanging, and you will be stuck on the particular
webpage and just loading and loading and so on.
When a request is received by Express, each middleware that matches the request is run in the order it is
initialized until there is a terminating action (like a response being sent).
So if an error occurs, all middleware that is meant to handle errors will be called in order until one of them does
not call the next() function call.
If you are building Node js Express Web Application, then you have already used an express middleware.
app.use(express.static('public'));
app.use(bodyParser.urlencoded({extended: true}));
app.use(bodyParser.json());
As we have discussed earlier, it has next() function is available. If I want to log anything for each incoming
request, then I need to write following code.
next();
Similarly, there is one Node package called Morgan which is doing the same thing. Log all the request in the
console. In actual Node js application, we can use the middleware like following.
console.log('caught intrusion');
next();
app.use(logger);
});
app.listen(3000);
Authentication Example
If we have to check whether the user has logged in or not for the current route or try to access authenticated
route, then we can write the following tutorial.
res.render('pages/index');
});
if(req.isAuthenticated()){
next();
else{
res.redirect("/users/login");
In above example is also called “Route Middleware.” It checks the request to see if the user is authenticated
or not. Based on the function response, it will redirect the user accordingly.
Configurable middleware
If you need your middleware to be configurable, export a function which accepts an options object or other
parameters, which, then returns the middleware implementation based on the input parameters.
// First.middleware.js
module.exports = function(options) {
next();
Now, use this middleware in our server.js file or name it as you want.
// server.js
var Fm = require('./First.middleware.js');
Bind application-level middleware to an instance of the app object by using the app.use().
This example shows a middleware function with no mount path. Every time user sends a request, the middleware
function always runs.
console.log('caught intrusion');
next();
app.use(logger);
});
Router-level middleware
console.log('Time:', Date.now());
next();
});
Error-handling middleware
In order to call an error-handling middleware, you simply pass the error to next(), like this:
Note: Error-handling middleware always takes four arguments. You must provide four arguments to identify it as
an error-handling middleware function. Even if you don’t need to use the next object, you must specify it to
maintain the signature. Otherwise, the next object will be interpreted as regular middleware and will fail to handle
errors.
})
In order to call an error-handling middleware, you simply pass the error to next(), like this:
});
console.log(err);
if(!res.headersSent){
res.status(500).send(err.message);
});
In this case, the error handling middleware at the end of the pipeline will handle the error. You might also notice
that I checked the res.headersSent property. This just checks to see if the response has already sent the headers
to the client. If it hasn’t it sends a 500 HTTP status and the error message to the client. You can also chain error-
handling middleware. This is common to handle different types of errors in different ways. For instance:
err.httpStatusCode = 404;
next(err);
});
err.httpStatusCode = 304;
next(err);
});
res.status(400).send('NotFound');
next(err);
});
res.status(304).send('Unauthorized');
next(err);
})
// catch all
console.log(err);
if (!res.headersSent) {
res.status(err.httpStatusCode || 500).send('UnknownError');
});
In this case, the middleware checks to see if a 404 (not found) error was thrown. If so, it renders the ‘NotFound’
template page and then passes the error to the next item in the middleware. The next middleware checks to see
if a 304 (unauthorized) error was thrown. If it was, it renders the ‘Unauthorized’ page, and passes the error to
the next middleware in the pipeline. Finally the “catch all” error handler just logs the error and if no response has
been sent, it sends the error’s httpStatusCode (or an HTTP 500 status if none is provided) and renders the
‘UnknownError’ template.
Built-in middleware
express.static serves static assets such as HTML files, images, and so on.
Third-party middleware
Install the Node.js package for the required functionality, then load it in your application at the application level
or the router level. We have already seen the body-parser middleware, which is third-party middleware.
The following example illustrates installing and loading the cookie-parsing middleware function cookie-parser.
app.use(cookieParser())
4.2 Configuration
When building any kind of server side app, you will eventually have the following thoughts:
I would like to use a third-party service through a provided API key or credentials.
Given values to these kinds of parameters will yield a configuration. It is a crucial part of the system as our
application will have lots of configurable elements, so it must be handled properly throughout the codebase.
We use third party module debug to log messages depending on workspace settings. Eg
startupDebug('Morgan enabled.....')
dbDebug('dbconnected..........')
then set a variable called DEBUG and give it either of the two workspaces or both
export DEBUG= app:startup,app:db --- will allow both startupDebug and dbDebug messages to be displayed
export DEBUG =app:* --- this enables all debug messages for any workspace starting with app: to be displayed
CHAPTER FIVE: Callbacks, Promises, and Async
Synchronous operations in JavaScript entails having each step of an operation waits for the previous step to
execute completely. This means no matter how long a previous process takes, subsquent process won't kick off
until the former is completed. Asynchronous operations, on the other hand, defers operations. Any process that
takes a lot of time to process is usually run alongside other synchronous operation and completes in the future.
This lesson dwells on fundamental concepts that JavaScript relies on to handle asynchronous operations. These
concepts include: Callback functions, Promises and the use of Async and Await to handle deferred operations
in JavaScript.
Asynchronous Operations
Operations in JavaScript are traditionally synchronous and execute from top to bottom. For instance, a farming
operation that logs farming process to the console:
console.log("Plant corn");
console.log("Water plant");
console.log("Add fertilizer");
If we run the code above, we have the following logged in the console:
Plant corn
Water plant
Add fertilizer
Now let's tweak that a bit so that watering the farm take longer than planting and fertilizing:
console.log("Plant maize");
setTimeout(function() {
console.log("Water plant")
},3000);
console.log("Add fertilizer");
We get the following in the console:
Plant Maize
Add fertilizer
Water plant
Why? The setTimeout function makes the operation asynchronous by deferring plant watering to occur after 3
seconds. The whole operation doesn’t pause for 3 seconds so it can log “Water plant”. Rather, the system goes
ahead to apply fertilizers and then water plant after 3 seconds.
When a function simply accepts another function as an argument, this contained function is known as a callback
function. Using callback functions is a core functional programming concept, and you can find them in most
JavaScript code; either in simple functions like setInterval, event listening or when making API calls.
setInterval(function() {
console.log('hello!');
}, 1000);
setInterval accepts a callback function as its first parameter and also a time interval. Another example
using .map();
Multiple functions can be created independently and used as callback functions. This is called multi-level
functions. When this function tree created becomes too large, the code becomes incomprehensible sometimes
and is not easily refactored. This is known as callback hell. Let’s see an example:
5.3 Promises
A promise is used to handle the asynchronous result of an operation. JavaScript is designed to not wait for an
asynchrnous block of code to completely execute before other synchronous parts of the code can run. For
instance, when making API requests to servers, we have no idea if these servers are offline or online, or how
long it takes to process the server request.
With Promises, we can defer execution of a code block until an async request is completed. This way, other
operations can keep running without interruption.
Pending: This is the initial state of the Promise before an operation begins
Fulfilled: This means the specified operation was completed
Rejected: The operation did not complete; an error value is usually thrown
Creating a Promise
The Promise object is created using the new keyword and contains the promise; this is an executor function
which has a resolve and a reject callback. As the names imply, each of these callbacks returns a value with
the reject callback returning an error object.
resolve(dateDetails)
} else {
reject(new Error('Bad weather, so no Date'))
}
});
If weather is true, resolve the promise returning the data dateDetails, else return an error object with data Bad
weather, so no Date.
Using Promises
Using a promise that has been created is relatively straightforward; we chain .then() and .catch() to our Promise
like so:
date
.then(function(done) {
// the content from the resolve() is here
})
.catch(function(error) {
// the info from the reject() is here
});
Using the promise we created above, let's take this a step further:
myDate();
Since the weather value is true, we call mydate() and our console logs read:
Note: Promises are asynchronous. Promises in functions are placed in a micro-task queue and run when other
synchronous operations complete.
Chaining Promises
Sometimes we may need to execute two or more asynchronous operations based on the result of preceding
promises. In this case, promises are chained. Still using our created promise, let’s order an uber if we are going
on a date.
So we create another promise:
resolve(message)
});
}
This promise can be shortened to:
myDate();
Since our weather is true, the output to our console is:
An async function is a modification to the syntax used in writing promises. You can call it syntactic sugar over
promises. It only makes writing promises easier.
An async function returns a promise -- if the function returns a value, the promise will be resolved with the
value, but if the async function throws an error, the promise is rejected with that value. Let’s see an async
function:
function yourRide() {
return Promise.resolve('2017 Dodge Charger');
}
From the above statements, myRide() and yourRide() are equal and will both resolve to 2017 Dodge Charger.
Also when a promise is rejected, an async function is represented like this:
function foo() {
return Promise.reject(25)
}
// is equal to
async function() {
throw 25;
}
5.5 Await
Await is only used with an async function. The await keyword is used in an async function to ensure that all
promises returned in the async function are synchronized, ie. they wait for each other. Await eliminates the use
of callbacks in .then() and .catch(). In using async and await, async is prepended when returning a promise,
await is prepended when calling a promise. try and catch are also used to get the rejection value of an async
function. Let's see this with our date example:
} catch(error) {
console.log(error.message);
}
}
Lastly we call our async function:
myDate();