JavaScript

JavaScript Objects and Prototypes

JavaScript objects are containers for named values, called properties, and methods.

Every JavaScript Object has a prototype which they inherit the properties and methods from. All JavaScript objects inherit from the Object.prototype. The standard way to create an object prototype is to use an Object Constructor function:

// It is considered good practice to name constructor functions with an upper-case first letter.
function Person(first, last, age, eye) {
    this.firstName = first;
    this.lastName = last;
    this.age = age;
    this.eyeColor = eye;
}

//With a constructor function, you can use the new keyword to create new objects from the same prototype:
var myFather = new Person("John", "Doe", 50, "blue");
var myMother = new Person("Sally", "Rally", 48, "green");

                    

The JavaScript prototype property allows you to add new properties to an existing prototype:

function Person(first, last, age, eyecolor) {
    this.firstName = first;
    this.lastName = last;
    this.age = age;
    this.eyeColor = eyecolor;
}

Person.prototype.nationality = "English";

Person.prototype.name = function() {
    return this.firstName + " " + this.lastName;
};

                    

Hoisting

During compilation, var and function declarations are "moved" to the top of their respective scopes. This behaviour is called hoisting.

This means declared functions can be called before it is defined. Function expressions are not hoisted and are only usable after they are created when the execution reaches it.

var declarations are hoisted but not their assignments. let and const are not hoisted.

// only 'var g' is hoisted, not its assignment
var g = 34;

// function declaration are hoisted
function sum(a, b) {
    return a + b;
}

// function expression are not hoisted, only 'var sum' is hoisted
var sum = function(a, b) {
    return a + b;
}

                    

Array Operations

push()

Adds new items to the end of an array.

var arr = [1,2,3];
arr.push(4);
// arr is now [1,2,3,4]

                    

pop()

Removes the last element of an array.

var arr = [1, 4, 6].pop();
// array becomes [1, 4]
                    

shift()

Removes first item of an array.

var oneDown = [1, 4, 6].shift();
// array becomes [4, 6]

                    

unshift()

Adds items to the beginning of an array.

var arr = [1,2,3];
arr.unshift(4);
// arr is now [4, 1, 2, 3]

                    

slice()

Returns the selected elements in an array, as a new array object.

var fruits = ["Banana", "Orange", "Lemon", "Apple", "Mango"];
var citrus = fruits.slice(1, 3);
// citrus is ["Orange", "Lemon"]

                    

forEach()

Calls a provided function once for each element in an array, in order.

var colors = ['red','blue','green'];

function print(val){
    console.log(val);
}

colors.forEach(print);

//red
//blue
//green
                    

map()

Calls a provided function once for each element in an array, in order, creating a new array

var colors = ['red','blue','green'];

function capitalize(val){
    return val.toUpperCase();
}

var capitalizedColors = colors.map(capitalize);

//["RED", "GREEN, "BLUE"]
                    

filter()

Creates an array filled with all array elements that pass a test (provided as a function).

var value = [1, 60, 34, 30, 20, 5]

function lessThan20(val){
    return val < 20;

}

var valueLessThan20 = values.filter(lessThan20);

//[1, 5]
                    

find()

Returns the value of the first element in an array that passes a test (provided as a function).

var ages = [3, 10, 18, 20];

function checkAdult(age) {
    return age >= 18;
}

var firstAdult = ages.find(checkAdult);
//18
                    

every()

Checks if all elements in an array passes a test (provided as a function) and returns true or false

var ages = [32, 33, 16, 40];

function checkAdult(age) {
    return age >= 18;
}

console.log(ages.every(checkAdult));

//false
                    

some()

Checks if any of the elements in an array passes a test (provided as a function) and returns true or false

var ages = [3, 10, 18, 20];

function checkAdult(age) {
    return age >= 18;
}

console.log(ages.some(checkAdult));

//true
                    

reduce()

Executes a provided function for each value of the array and reduces the array to a single value.

var numbers = [65, 44, 12, 4];

function getSum(total, num) {
    return total + num;
}

console.log(ages.reduce(getSum));

//125
                    

Value vs. Reference

Primatives, like null, undefined , boolean, number, string are assigned by value-copy. Compound values (objects, which includes arrays, and functions), always create a copy of the reference on assignment.

var a = 2;        // 'a' hold a copy of the value 2.
var b = a;        // 'b' is always a copy of the value in 'a'
b++;

console.log(a);   // 2
console.log(b);   // 3

var c = [1,2,3];
var d = c;        // 'd' is a reference to the shared value

d.push( 4 );      // Mutates the referenced value (object)
console.log(c);   // [1,2,3,4]
console.log(d);   // [1,2,3,4]

/* Compound values are equal by reference */
var e = [1,2,3,4];
console.log(c === d);  // true
console.log(c === e); // false

                    

JavaScript Closures

A JavaScript closure is a self-invoking function that makes it possible for a function to have "private" variables even after the parent function has closed.

A counter example to explain JavaScript closures

Say you wanted to count the number of times a user clicked on a button on a webpage and you are triggering a function on the onclick event of the button to update the count variable.

‹button onclick="updateClickCount()"› click me ‹⁄button›

You could use a global variable and an updateClickCount() function to increase the counter. However this could cause issues as any script on the page can change the counter, without calling updateClickCount().

var counter = 0;

function updateClickCount() {
  ++counter;
  // do something with counter
}
                    

If you declared the variable inside the function (i.e. make it a variable local to the function) to avoid this, it would mean that the counter variable is set to 1 everytime the button was clicked as local variables are created when the function is invoked, and deleted when the function is finished.

function updateClickCount() {
    let counter = 0;
    ++counter;
    // do something with counter
}

                    

Using a closure (self-invoking function) will enable us to solve this issue.

var updateClickCount=(function(){
   let counter=0;

   return function(){
    ++counter;
    // do something with counter
   }
})();

                    

The self-invoking function only runs once. It sets the counter to 0, and returns a function expression.

This way updateClickCount becomes a function. The "wonderful" part is that it can access the counter in the parent scope. This is called a JavaScript closure. It makes it possible for a function to have "private" variables. The counter is protected by the scope of the anonymous function, and can only be changed using the updateClickCount function.

Call, Apply and Bind

Call, Apply and Bind are function methods that allow you to change the value of 'this' for a given function.

call() and apply()

Call and Apply are invoked immediately. Call takes any number of parameters: 'this', followed by the additional arguments. Apply takes only two parameters: 'this', followed by an array of the additional arguments.

In the below example, when we use add.call() the first parameter is what 'this' should be bound to. The subsequent parameters are passed into the function we are calling. Thus, in add(), this.a refers to ten.a and this.b refers to ten.b and we get 1+2+3+4 returned, or 10.

add.apply() is similar. The first parameter is what 'this' should be bound to. The subsequent parameter is an array of arguments to be used in the function.


function add(c, d) {
    console.log(this.a + this.b + c + d);
}

var ten = {a: 1, b: 2};

add.call(ten, 3, 4);
// logs => 10

add.apply(ten, [3,4]);
// logs => 10

                    

bind()

With bind() the parameters are identical to call() but bind() is not invoked immediately. Instead, bind() returns a function with the context of 'this' bound already. Because of this, bind() is useful when we don’t know all of our arguments up front.


var small = {
    a: 1,
    go: function(b,c,d){
        console.log(this.a+b+c+d);
    }
}

var large = {
    a: 100
}

// small.go is called normally
small.go(2,3,4);
// logs 1+2+3+4 => 10

// if we want to use large.a we can use call()
small.go.call(large,2,3,4);
// logs 100+2+3+4 => 109

// if we don't know what the other two arguments are yet we can use bind
var bindTest = small.go.bind(large,2);

// we can call it later with the other two arguments
bindTest(3,4);
// logs 100+2+3+4 => 109

                    

Callbacks And Higher-Order Functions

A callback function is a function that is passed to another function (higher-order function) as an argument and can be executed or returned later by the higher-order function. When a callback function is passed as an argument, only the function definition is passed. The function is not executed.

When we pass a callback function as an argument to another function, the callback is executed at some point inside the containing function’s body just as if the callback were defined in the containing function.

This means the callback is a closure. Closures have access to the containing function’s scope, so the callback function can access the containing function's variables, and even the variables from the global scope.

The below is a quick example:

function greeting(name) {
    alert('Hello ' + name);
}

function processUserInput(callback) {
    var name = prompt('Please enter your name.');
    callback(name);
}

processUserInput(greeting);

                    

JQuery uses callback functions extensively. Below is an example:

//The item in the click method's parameter is a callback function, not a variable.​
$("#btn_1").click(function() {
    alert("Btn 1 Clicked");
});

                    

Asynchronous Callbacks

Callbacks are often used to continue code execution after an asynchronous operation has completed.

Consider the code below:


function first(){
    console.log(1);
}

function second(){
    console.log(2);
}

                    

If we invoke our functions we get the following:

first();
second();

// 1
// 2

                    

However, what if function first contains code that can't be executed straight way? For example, an API request where we have to send the request then wait for a response?

The below simulates this action:

function first(){
    // Simulate a code delay
    setTimeout( function(){
        console.log(1);
    }, 500 );
}

function second(){
    console.log(2);
}

                    

We get the following when we invoke our functions:

first();
second();

// 2
// 1

                    

So even though we invoked the first() function first, the result got logged out after that of the second() function.

JavaScript didn’t wait for a response from first() before moving on to execute second(). You can’t just call one function after another and hope they execute in the right order. Callbacks are a way to make sure certain code doesn’t execute until other code has already finished execution.

function first(callback){

    // Simulate a code delay
    setTimeout( function(){
        console.log(1);

        //execute callback
        callback();
    }, 1000 );
}

function second(){
    console.log(2);
}


                    

We now get the following when we invoke our functions:

first(second);

// 1
// 2

                    

The use of Callback functions is useful for HTTP requests (like making an AJAX request) where you need to do something with the response where you can use a callback function to handle the response whenever it arrives.

Another useful example in this context could be when your application is dependent on user input or when reading from files or when downloading things.

In the below example, T.get simply means we are making a get request to Twitter. There are three parameters in this request: ‘search/tweets’, which is the route of our request, params which are our search parameters, and then an anonymous function which is our callback.

T.get('search/tweets', params, function(err, data, response) {
    if(!err){
        // This is where the magic will happen
    } else {
        console.log(err);
    }
})
                    

A callback is important here because we need to wait for a response from the server before we can move forward in our code. We don’t know if our API request is going to be successful or not so after sending our parameters to search/tweets via a get request, we wait.

Once Twitter responds, our callback function is invoked. Twitter will either send an err (error) object or a response object back to us.

In our callback function we can use an if() statement to determine if our request was successful or not, and then act upon the new data accordingly.

JavaScript Modules

Introduction

Modules help developers separate functionality and organize the codebase. Also without modules JavaScript's global namespace can become easily polluted.

Functions are the only thing in JavaScript that create a new scope; thus anything not declared within a function is a part of the global namespace.

Thus every function we declare in JavaScript is available globally and therefore its name cannot be used again. Often this leads to a polluted namespace where names and code can be difficult to find and reuse.

Sometimes we don’t want that function to be available everywhere. In order to make functions in one JavaScript file available to just a single other JavaScript file, we can use modules.

There is no native support for a modules system in ES5. Fortunately there are non native ways to emulate a modules system. Two of the popular ones are CommonJS whose syntax will be familiar to those who work in the Node.JS ecosystem, and AMD (Asynchronous module definition).

CommonJS

A CommonJS module is essentially a reusable piece of JavaScript which exports specific objects, making them available for other modules to require in their programs.

With CommonJS, each JavaScript file stores modules in its own unique module context (just like wrapping it in a closure). In this scope, we use the module.exports object to expose modules, and require to import them.

Below is an example CommonJS module in a file called myModule.js

function myModule() {
    this.hello = function() {
        return 'hello!';
    }

    this.goodbye = function() {
        return 'goodbye!';
    }
}

module.exports = myModule;

                    

If myModule needs to be used, you can require it in your file:

var myModule = require('myModule');

var myModuleInstance = new myModule();
myModuleInstance.hello(); // 'hello!'
myModuleInstance.goodbye(); // 'goodbye!'

                    

Module Bundling

If you are using non-native module systems like CommonJS (which browsers can't interpret), you will need to use a specialized tool to convert your modules into properly-ordered browser-friendly code.

Also another issue with using CommonJS is that it loads modules synchronously. This will be an issue in the browser as the browser will be blocked from running anything else until it finishes loading modules.

To get around these issues, we must "bundle" the modules. Module bundling is the process of concatenating a group of modules and their dependencies into a single file (or group of files) in the correct order, in order to reduce the number of browser requests.

Two of the most popular options are Browserify and Webpack. Please see section on Module Bundling.

Browserify

Consider the below main.js file that requires (imports) a module 'myDependency'

var myDependency = require(‘myDependency’);

var myGrades = [93, 95, 88, 0, 91];

var myAverageGrade = myDependency.average(myGrades);

                    

We have one dependency. Using the command below, Browserify recursively bundles up all the required module(s), in the correct order, starting at main.js into a single file called bundle.js:

browserify main.js -o bundle.js

                    

All you have to do is insert a single script tag with your bundle.js file into your html to ensure that all of your source code is downloaded in one HTTP request.

If you have multiple files with multiple dependencies, you simply tell Browserify what your entry file is.

The final step would be Minification of the bundled code which is the process of removing unnecessary characters from source code e.g. whitespace, comments, new line characters, etc., in order to reduce the overall size of the content without changing the functionality of the code.

Webpack

Webpack is another bundler that can be used. Webpack provides some useful features like code splitting, a way to split your codebase into chunks which are loaded on demand.

For example, if you have a web app with blocks of code that are only required under certain circumstances, it might not be efficient to put the whole codebase into a single massive bundled file. In this case, you could use code splitting to extract code into bundled chunks that can be loaded on demand, avoiding trouble with big up-front payloads when most users only need the core of your application.

ES6 Const And Let Variables

Scope refers to where in your program your variables are visible.

Let and Const Variables have block scope (surrounded by {}) and can't be used before declaration.

Var variables have function scope and are hoisted to the top which means can be used before it has been declared.

function f() {
    var x = 1;
    let y = 2;
    const z = 3;
    {
        var x = 100;
        let y = 200;
        const z = 300;
        console.log('x in block scope is', x); // x in block scope is 100
        console.log('y in block scope is', y); // y in block scope is 200
        console.log('z in block scope is', z); // z in block scope is 300
    }
    console.log('x outside of block scope is', x); // x outside of block scope is 100
    console.log('y outside of block scope is', y); // y outside of block scope is 2
    console.log('z outside of block scope is', z); // z outside of block scope is 3
}

f();


                    

Const variables need to be declared using an initializer, or it will generate an error. They also cannot be reassigned entirely to a different value.

const i = 0;
i = 1; //  TypeError: Assignment to constant variable.
                    

However, if the value of a const is an object or array, the object’s properties themselves are still mutable, i.e. they are able to be modified.

const obj = {
    i: 0
};
obj.i = 1;
console.log(obj.i); // 1


                    

ES6 Arrow Functions

Introduction

Two factors influenced the introduction of arrow functions: shorter function syntax and non-binding of 'this'. It is important to note that arrow functions cannot be used as constructors as other functions can.

Shorter Function Syntax

Consider the following function

function funcName(param1, param2) {
   return param1 + param2;
 }

funcName(2, 2);
// returns 4

                    

Using the new arrow function syntax, it can now be written in the following way:

var funcName = (param1, param2) => { param1 + param2 };

funcName(2, 2);
// returns 4
                    

If a function has only one parameter, the parenthesis are optional:

function funcName(param1) {
   return param1 + 2;
 }

funcName(2);
// returns 4

// With arrow function:
var funcName = param1 => { param1 + 2 };

funcName(2);
// returns 4

// It can also be written like the following as we are returning an expression:
var funcName = param1 => param1 + 2 ;

funcName(2);
//returns 4

                    

The paramter list for a function with no parameters should be written with a pair of parenthesis:

() => { statements };
                    

The body of the function should be parenthesized if it is returning an object literal expression

params => ({foo: bar});
                    

No Binding Of 'This'

An arrow function does not bind 'this'. Arrows instead bind 'this' to the immediate enclosing lexical scope. Therefore, you don’t need bind() or that = this, anymore.

Consider the following, in non-strict mode, the growUp() function defines 'this' as the global object, which is different from the 'this' defined by the Person() constructor. In strict mode, this is undefined.

function Person() {
  // The Person() constructor defines `this` as an instance of itself.
  this.age = 0;

  setInterval(function growUp() {
    this.age++;
    console.log(this.age)
  }, 1000);
}

var p = new Person();
// The above will return NaN every 1000 milliseconds because
// the growUp() function defines `this` as the global object

                    

In ECMAScript 3/5, the 'this' issue was fixable by assigning the value in 'this' to a variable that could be closed over.

function Person() {
  var that = this;
  that.age = 0;

  setInterval(function growUp() {
    // The callback refers to the `that` variable of which
    // the value is the expected object.
    that.age++;
    console.log(that.age);
  }, 1000);
}

var p = new Person();
// The above will return a number incremented by 1 every second.

                    

An arrow function does not have its own 'this'; the 'this' value of the enclosing execution context is used. Thus, in the following code, the this within the function that is passed to setInterval has the same value as 'this' in the enclosing function:

function Person(){
  this.age = 0;

  setInterval(() => {
    this.age++; // |this| properly refers to the person object
    console.log(this.age);
  }, 1000);
}

var p = new Person();
// The above will return a number incremented by 1 every second.

                    

ES6 Default Parameters

Allows missing or undefined parameter values to be initialised with a default value, function or expression:

//With values:

function foo( a = 5, b = 10) {
    console.log( a + b);
}
foo();  // 15
foo( 7, 12 );  // 19
foo( undefined, 8 ); // 13
foo( 8 ); // 18
foo( null ); // 10 as null or false values are equated as 0

//With function and expression:

function foo( a ) { return a * 4; }
function bar( x = 2, y = x + 4, z = foo(x)) {
    console.log([ x, y, z ]);
}
bar();  // [ 2, 6, 8 ]
bar( 1, 2, 3 ); //[ 1, 2, 3 ]
bar( 10, undefined, 3 );  // [ 10, 14, 3 ]

                    

ES6 Template Literals

Template literals are started with a back tick and can have variables, equations or function calls inserted in curly brackets:

var country = 'France', capital = 'Paris', continent = 'Europe';

// ES5
var a = 'Hi, I\'m in ' + capital + ' capital of ' + country + ', it\'s in ' + continent + '.';

// ES6
var b = `Hi, I'm  in ${ capital } capital of ${ country }, it's in ${ continent }.`;


                    

ES 6 Spread Operator / Rest Parameter

Spreading an array or object into a new array or object:

let a = [3, 4, 5];
let b = [1, 2, ...a, 6];
console.log(b);  // [1, 2, 3, 4, 5, 6]

let car = { type: 'vehicle ', wheels: 4};
let fordGt = { make: 'Ford', ...car, model: 'GT'};
console.log(fordGt); // {make: 'Ford', type: 'vehicle', wheels: 4, model: 'GT'}

                    

Rest Parameter. Getting the rest of an array into a new array:

[a, b, ...rest] = [10, 20, 30, 40, 50];

console.log(rest);
// expected output: [30,40,50]

                    

Gathering variables into an array, which is useful for when you don't know how many variables are being passed to a function:

function foo(...args) {
    console.log(args);
}

foo( 'car', 54, 'tree');  //  [ 'car', 54, 'tree' ]

                    

ES6 Destructuring Assignment

Makes it possible to unpack values from arrays, or properties from objects, into distinct variables:

let a, b, rest;

[a, b] = [10, 20];

console.log(a);
// expected output: 10

console.log(b);
// expected output: 20


// rest operator
[a, b, ...rest] = [10, 20, 30, 40, 50];

console.log(rest);
// expected output: [30,40,50]

let { a, c } = {a: 1, b: 2, c: 3};
console.log(a); // 1
console.log(c); // 3
console.log(b); // undefined

// Round braces ( ... ) around the assignment statement is required syntax
// when using object literal destructuring assignment without a declaration.
let a, c;
({ a, c } = {a: 1, b: 2, c: 3});
console.log(a); // 1
console.log(c); // 3
console.log(b); // undefined

// Assigning to new variable names
var o = {p: 42, q: true};
var {p: foo, q: bar} = o;

console.log(foo); // 42
console.log(bar); // true


                    

ES6 Dynamic Property Names

Allows the ability to create or add properties with dynamically assigned keys:

let  city= 'leicester_';
let a = {
    [ city + 'population' ]: 500000
};

a[ city + 'county' ] = 'Leicestershire';
console.log(a); // {leicester_population: 350000, leicester_county: 'Leicestershire' }


                    

ES6 Classes

Introduction

It is important to note that JavaScript’s Class syntax in ES6 is little more than syntactic “sugar over” for the existing prototype linkage based behaviour.

JavaScript’s classes aren’t like classes in languages like Java or Python. In these “class based” languages you write classes which are then used as templates for creating new objects.

In class based languages you instantiate the class to create a new object. The methods and properties of the class are copied into a new entity, called an instance.

This instance is your object and after it has been initiated it has no active relation with, i.e. is independent of, its parent class.

JavaScript doesn’t have such copy mechanics. “Instantiating” a class in JavaScript does create a new object, but not one that is independent of its “parent class”.

Instead, it creates an object that is linked to the parent’s prototype and, even after instantiation, changes to the parent’s prototype propagate to the new object.

A simple example that uses the ES6 Class syntax:

"use strict";

// This is a base class
class Food {
    constructor (name, protein, carbs, fat) {
        this.name = name;
        this.protein = protein;
        this.carbs = carbs;
        this.fat = fat;
    }

    toString () {
        return `${this.name} | ${this.protein}g Prot :: ${this.carbs}g Carbs :: ${this.fat}g Fat`
    }

    print () {
        console.log( this.toString() );
    }
}

const fish_fillet = new Food('Fish Fillet', 30, 0, 5);
fish_fillet.print(); // Outputs 'Fish Fillet | 30g Prot :: 0g Carbs :: 5g Fat'

                    

Equivalent using pre ES6 syntax:

"use strict";

function Food(name, protein, carbs, fat) {
    this.name = name;
    this.protein = protein;
    this.carbs = carbs;
    this.fat = fat;
    this.toString = function () {
        return `${this.name} | ${this.protein}g Prot :: ${this.carbs}g Carbs :: ${this.fat}g Fat`
    };
}

Food.prototype.print = function print() {
    console.log( this.toString() );
}

const fish_fillet = new Food('Fish Fillet', 30, 0, 5);
fish_fillet.print(); // Outputs 'Fish Fillet | 30g Prot :: 0g Carbs :: 5g Fat'

                    

Inheritance using new ES6 Class syntax

With the new syntax, derived classes are created using the extend keyword and any reference to the base class can be done so using the super keyword.

"use strict";

// Food is a base class
class Food {
    constructor (name, protein, carbs, fat) {
        this.name = name;
        this.protein = protein;
        this.carbs = carbs;
        this.fat = fat;
    }

    toString () {
        return `${this.name} | ${this.protein}g Prot :: ${this.carbs}g Carbs :: ${this.fat}g Fat`
    }

    print () {
        console.log( this.toString() );
    }
}

// FatFreeFood is a derived class
class FatFreeFood extends Food {
    constructor (name, protein, carbs) {
        // calls the contructor of the base class that it extends
        super(name, protein, carbs, 0);
    }

    print () {
        // calls the print function of the base class
        super.print();
    }
}

const natural_yogurt = new FatFreeFood('Natural Yogurt', 20, 13);
natural_yogurt.print(); //Outputs  'Natural Yogurt | 20g Prot :: 13g Carbs :: 0g Fat

                    

Equivalent using pre ES6 syntax:

"use strict";

function Food(name, protein, carbs, fat) {
    this.name = name;
    this.protein = protein;
    this.carbs = carbs;
    this.fat = fat;
    this.toString = function () {
        return `${this.name} | ${this.protein}g Prot :: ${this.carbs}g Carbs :: ${this.fat}g Fat`
    };
}

Food.prototype.print = function print() {
    console.log( this.toString() );
}

function FatFreeFood (name, protein, carbs) {
    Food.call(this, name, protein, carbs, 0);
}

// set FatFreeFood's prototype to Food's proptype
FatFreeFood.prototype = Object.create(Food.prototype);

const natural_yogurt = new FatFreeFood('Natural Yogurt', 20, 13);
natural_yogurt.print(); // Outputs 'Natural Yogurt | 20g Prot :: 13g Carbs :: 0g Fat

                    

Resources

A Deep Dive into Classes

ES6 Promises

Promises give us a way to handle asynchronous processing in a more synchronous fashion.

Creating Promises

Promises are created by using the promise constructor which takes one argument, a callback function.

The callback function can take two handler arguments, one is typically named resolve, which is called if everything worked as expected, and the other is typically named reject if not.

A Promise can have one of the following three states:

  • Pending - until a Promise is fulfilled, it is in the pending state
  • Fulfilled - when the first handler is called the Promise is considered fulfilled with the value passed to that handler.
  • Rejected - if the second handler is called, the Promise is considered rejected with the value passed to that handler.

Processing Promises

To process (consume) a Promise, we use its .then() method. This method takes two possible handler parameters. The first is the function called if the Promise is fulfilled and the second is called if the Promise is rejected.

A Promise can only be 'settled' (fulfilled or rejected) once. Other consumers can not change the settled value.

Example Using readFile

import {readFile} from 'fs';

function readFilePromisified(filename) {
    return new Promise(
        function (resolve, reject) {
            readFile(filename, { encoding: 'utf8' },
            (error, data) => {
                if (error) {
                    reject(error);
                } else {
                    resolve(data);
                }
            });
        }
    );
}

// readFilePromisified() is used like this:

readFilePromisified(process.argv[2])
.then(text => {
    console.log(text);
})
.catch(error => {
    console.log(error);
});

                    

Example Using XMLHttpRequest

function get(url) {
    // Return a new promise.
    return new Promise(function(resolve, reject) {
        // Do the usual XHR stuff
        var req = new XMLHttpRequest();
        req.open('GET', url);

        req.onload = function() {
            // This is called even on 404 etc
            // so check the status
            if (req.status == 200) {
                // Resolve the promise with the response text
                resolve(req.response);
            }
            else {
                // Otherwise reject with the status text
                // which will hopefully be a meaningful error
                reject(Error(req.statusText));
            }
        };

        // Handle network errors
        req.onerror = function() {
            reject(Error("Network Error"));
        };

        // Make the request
        req.send();
    });
}

//Now let's use it:

get('story.json').then(function(response) {
    console.log("Success!", response);
})
.catch(error => {
    console.error("Failed!", error);
});

                    

Promise.all and Promise.race

Promise.all is a way for running an array of Promises concurrently. Note that even if a single dependency is rejected, the Promise.all method will be rejected entirely as well. If all promises are resolved, correctly Promise.all triggers its then handler with an array of resolved values in the same order as promises passed in.

// list of files we want to read from disk
let filenames = ['index.html', 'blog.html', 'terms.html'];

Promise.all(filenames.map(readFilePromise))
.then(files => {
    console.log('index:', files[0]);
    console.log('blog:', files[1]);
    console.log('terms:', files[2]);
})

                    

Promise.race is similar to Promise.all but will return once ANY of the Promises resolves or rejects, discarding all the other results. This could be useful for scenarios where we want to time out a promise we otherwise have no control over.

var p = Promise.race([
        fetch('/resource-that-may-take-a-while'),
        new Promise(function (resolve, reject) {
        setTimeout(() => reject(new Error('request timeout')), 5000)
        })
])

p.then(response => console.log(response))
p.catch(error => console.log(error))

                    

Resources

JavaScript Promises
ES6 Promises
Promises Introduction
Patterns and Anti-Patterns
ES6 Promises in Depth

ES6 Modules

ES6 specification sees the introduction of built-in module support.

ES6 modules are stored in files with one module per file /one file per module.

Modules are singletons; even if a module is imported multiple times, only a single 'instance' of it exists.

Named Exports

A module can export multiple things by prefixing its declarations with the keyword export:

export const sqrt = Math.sqrt;
export function square(x) {
return x * x;
}

export function diag(x, y) {
return sqrt(square(x) + square(y));
}

//------ main.js ------
import { square, diag } from 'lib';
console.log(square(11)); // 121
console.log(diag(4, 3)); // 5


                  

You can also import the whole module:

import * as lib from 'lib';
console.log(lib.square(11)); // 121
console.log(lib.diag(4, 3)); // 5


                  

The same code in CommonJS

/------ lib.js ------
var sqrt = Math.sqrt;
function square(x) {
        return x * x;
}
function diag(x, y) {
        return sqrt(square(x) + square(y));
}
module.exports = {
sqrt: sqrt,
square: square,
diag: diag,
};

//------ main.js ------
var square = require('lib').square;
var diag = require('lib').diag;
console.log(square(11)); // 121
console.log(diag(4, 3)); // 5


                  

Default Exports

An ES6 module can pick a default export:

Default Function Export:

//------ myFunc.js ------
export default function () {} // no semicolon!

//------ main1.js ------
import myFunc from 'myFunc';
myFunc();


Default Class Export:

//------ MyClass.js ------
export default class {} // no semicolon!

//------ main2.js ------
import MyClass from 'MyClass';
const inst = new MyClass();
                  

In ES6, when you import modules, the import is resolved at compile time which allows the removal of exports that are not used by other modules (dead code elimination).

ES6 uses a 'tree shaking ‘approach to dead code elimination where it only includes code that your bundle needs to run rather than excluding code the your bundle doesn't need. In ES6, when you import modules, the import is resolved at compile time which allows the removal of exports that are not used by other modules (dead code elimination).

Building ES6 modules

Most browsers do not implement loading of ES6 modules so they must be built.

The most common approach for building/converting ES6 modules to work in the browser is to use a transpiler like Babel to transpile your ES6 code to ES5 code to CommonJS, then pipe the transpiled code through a module bundler like Browserify or Webpack to create one or more bundled files.

Polyfills and Transpilers

Transpilers take the syntax that older browsers don't understand (e.g. classes, 'const', arrow functions) and turn them into syntax that they will understand (functions, 'var', functions).

A polyfill is a type of shim that defines a new object or method in browsers that don't support that object or method (e.g. Array. prototype. includes, Map, Promise).

You can find a lot of what you will ever need in babel-polyfill or more specifically: core-js (which is what babel-polyfill uses).

Resources

Polyfills: everything you ever wanted to know, or maybe a bit less

Asynchronous HTTP request in JavaScript

JQuery $.ajax method

The $.ajax method can be used to request (get) data from or post data to a remote server.


jQuery Ajax

Fetch Web API

fetch is a powerful web API that lets you make asynchronous requests which returns a promise. The fetch function takes one required parameter: the endpoint URL. It also has other optional parameters.


Fetch Web API

Resources

HTTP request in JavaScript