Saturday, 12 August 2017

Multi-Threading with JavaScript Web Workers

Web Workers allow developers to create background threads which enables UI to run smoothly. This is useful for scenarios where the logical part of code is CPU intensive and it might slow down the UI.

Although the Web Workers can run code containing a wide variety of logic there are some restrictions that apply:
  • The DOM cannot be manipulated from inside the Web Worker.
  • The access to global variables and Objects(functions) from parent is restricted.
  • Limited access to window object. Read-only for window.location property.
  • No access tdocument and parent  object.
  • Newly spawned Web Workers can only be under the same origin as the parent.


Fig 1.0  Web Worker Types

There 5 Web Worker types that are laid out in Fig 1.0. But 'ChromeWorker' is specifc to only FireFox. The browser compatibility list can be found at http://caniuse.com/#feat=webworkers .

Although each Web Worker type has a crucial role to play in an application only Dedicated and Shared Workers have been discussed in this blog post.

Dedicated Worker:

Dedicated Web Workers cannot be shared by other scripts so they are only accessible by the script that created them. In this blog post a worker will be created to generate Fibonacci  sequence. Also the methods provided by the Worker object will be discussed. 

Spawn:

let fibWorker;

if(window.Worker){

    fibWorker = new Worker('fibonnaci.js');

}

A new Worker object can be spawned by calling the  new Worker(<file URI>) constructor. The <file URI> is the location of the file relative to the origin. 

Messaging:


The code run by the Worker cannot access functions from the caller script. So it has to communicate via messages. 

//main.js file
let listFibonacciNumbers = (n = 0) => {
   fibWorker.postMessage({'number': n});
}

fibWorker.onmessage = (e) =>{
     console.log('Fibonacci numbers:\n');
     console.log(e.data.numbers);
}

//------------------------------------------------------------------------------//

let generateFibonacciNumbers = (size) => {
   let n = size;
   let arr = [];

   for(let i =0; i<n ; i++){
     arr[i] = i<2?1: arr[i-2]+arr[i-1];
   }
    
   return arr;
}
//fibonacci.js onmessage = (e)=>{ let numbers = generateFibonacciNumbers(e.data.number); postMessage({'numbers':numbers}); }

The sample code above has two sections representing main.js and fiboanacci.js files. In each section there are methods that help the code communicate between two files.

In main.js file the listFibonacciNumbers method is used to send the Worker a message to start generating the numbers of size n . When the fibonacci number generation is completed the fiboanacci.js file sends back the array and fibWorker.onmessage callback will receive the array in main.js file. And any type of data can be sent back and forth between fiboanacci.js and main.js files using postMessage  method. This bidirectional messaging helps the UI thread to sync with the Worker thread.

Termination:

Terminating a worker form the main thread is rather simple. When the worker is terminated all its operations will cease and it will no longer be active.

fibWorker.terminate();

A worker can terminate itself by calling the following method from inside the worker code:

close();


Shared Worker :

In contrast to dedicated worker the shared worker can be accessed from any script which has the same origin as the worker script. This allows multiple windows, iframes and other workers to access it.


//main1.js
let inputNum = document.getElementById('fibnum');

if (!!window.SharedWorker) {
  let fibWorker = new SharedWorker("worker.js");

  inputNum.onchange = function() {
    fibWorker.port.postMessage({'size': inputNum.value});
  }

  fibWorker.port.onmessage = function(e) {
     console.log(e.data.fibArray);
  }
}


//main2.js
let inputNum = document.getElementById('fibnum');

if (!!window.SharedWorker) {
  let fibWorker = new SharedWorker("worker.js");

  inputNum.onchange = function() {
    fibWorker.port.postMessage({'size': inputNum.value});
  }

  fibWorker.port.onmessage = function(e) {
     console.log(e.data.fibArray);
  }
}

The sample codes above belong to two different files which are  main1.js  and  main2.js . In this case the codes for the two files perform the same function of requesting fibonacci sequence array. But  in general there can be many files that may ask a single worker to perform different tasks. UI thread can only communicate with the ServiceWorker  via specific port. In  main1.js and main2.js files the fibWorker.port.onmessage implicitly creates the port for communication. Using fibWorker.port.postMessage the main1.js and main2.js  files can communicate with the worker. When the result is received from the worker the fibWorker.port.onmessage gets the generated fibonacci array.

//workers.js
let generateFibonacci = (size) => {
   let n = size;
   let arr = [];

   for(let i =0; i<n ; i++){
     arr[i] = i<2?1: arr[i-2]+arr[i-1];
   }
    
   return arr;
}

onconnect (e) => {
  let port = e.ports[0];

  port.onmessage = function(e) {
    let fibArray = generateFibonacci(e.data.size);
        
    port.postMessage({arry: fibArray});
  }

}

The code above belongs to the worker.js file. It has a method called generateFibonacci which takes in  n as the argument to generate Fibonacci sequence of  that size. The onconnect method automatically fires as soon as the worker is created. Then using port.onmessage event it can receive the desired size of fibArray so it can send back the generated array using port.postMessage method.

Sunday, 23 July 2017

Map and Set Objects in ES6



Map is a useful tool in EcmaScript-6. It allows developers to store information using key-value pairs. In EcmaScript-5 the Object Literals also served a similar purpose but there are certain differences between the two which makes Map more efficient to use.


Map (EcmaScript -6)
Object Literal (EcmaScript- 5)
Can return the size of the map via ‘.size()’ method.
The object needs to be manually iterated over to get the overall size.
Can take object, function, string, Symbol and any primitive as keys.
Can only take string and symbol as keys.
Keys will not collide with prototype keys.
Keys might collide with prototype keys.

CRUD Operations

Create: 

let user_active_map = new Map();
user_active_map.set('user_01_active', true);

Creating a Map object is very simple. In the sample code  the variable 'user_active_map' is set to a new instance of Map. Then information is added to the Map object with key as 'user_01_active' and value as 'true'.

Read:

let user_active_map = new Map();
user_active_map.set('user_01_active', true);
user_active_map.set('user_02_active', true);
user_active_map.set('user_03_active', true);
user_active_map.set('user_04_active', true);
user_active_map.set('user_05_active', false);

console.log(user_active_map.get('user_05_active'));//false

The value can be read from a Map object using '.get(<key>)' method by passing the key as an argument. 


Update: 

let user_active_map = new Map();
user_active_map.set('user_01_active', false);

Similar to create the update operation can also use '.set(<key>, <value>)' method but then the value for the key will be overwritten. 

Delete:

let user_active_map = new Map();
user_active_map.set('user_01_active', true);
user_active_map.set('user_02_active', true);
user_active_map.set('user_03_active', true);
user_active_map.set('user_04_active', true);
user_active_map.set('user_05_active', false);

user_active_map.delete('user_05_active');

console.log(user_active_map.get('user_05_active'));//

Using the  '.delete(<key>)' method any value associated with a specific key can be deleted. 


WeakMap object shares CRUD methods with Map object such as  '.delete(<key>)', '.set(<key>, <value>)' and '.get(<key>, <value>)'. But WeakMap is much more efficient for certain use cases.

The WeakMap can only have object as keys and the keys are weakly referenced to the values. So if the object which is used as the key is destroyed then the entire entry is removed from the WeakMap.


let weak_map = new WeakMap();
let settings = {'volume': 5, 'now_playing':{'video_id': 1, 'video_speed': '1.5'}};

weak_map.set(settings.now_playing,true);

console.log(weak_map.get(settings.now_playing));//true

delete settings.now_playing;

console.log(weak_map.get(settings.now_playing));//undefined

In the sample code above when the WeakMap entry is set with the key as 'settings.now_playing' and value as 'true' it will persist only until the reference to the key is not destroyed. But when the key 'settings.now_playing' is deleted the entry associated with that key is also removed. 



The Set object stores collection of unique values of primitive type and object references. 

CRUD Operations

Create:


let user_arr = ['John','Peter','Peter','Phil'];
let user_Set = new Set(user_arr );

console.log(user_Set);//Set(3) {"John", "Peter", "Phil"}

To create a new instance of Set object the 'new Set(<Iterable>)' method can be used and in the sample code an array is passed as an argument. Passing an iterable argument loads the data into the Set which only contains unique values from the iterable.

Read:

let user_arr = ['John','Peter','Peter','Phil'];
let user_Set = new Set(user_arr );

for(let value of user_Set.values()){
    console.log(value); 
}
//John
//Peter
//Phil

The values in Set can be read by iterating over the iterable object returned by '.values()' method.

Update:


let user_arr = ['John','Peter','Peter','Phil'];
let user_Set = new Set(user_arr );

    user_Set.add('John');
    user_Set.add('Harry');

console.log(user_Set);//Set(4) {"John", "Peter", "Phil", "Harry"}

The set can be updated with new values using '.add(<value>)' method. Notice that adding duplicate value 'John' is ignored and the resulting 'user_Set' only has unique values.

Delete:


let user_arr = ['John','Peter','Peter','Phil'];
let user_Set = new Set(user_arr );

    user_Set.delete('John');
    user_Set.delete('Harry');

console.log(user_Set);//Set(2) {"Peter", "Phil"}

Using the '.delete(<value>)' method values can be deleted from the Set object. 


WeakSet object also shares the same CRUD operations as Set object with the exception to  '.values()' method. But the WeakSet differs slightly because the type of values it can take can only be 'object' and those values are weakly referenced. 


let weak_Set = new WeakSet();
let settings = {'volume': 5, 'now_playing':{'video_id': 1, 'video_speed': '1.5'}};

    weak_Set.add(settings.now_playing); 

    console.log(weak_Set.has(settings.now_playing));//true

    delete settings.now_playing;

    console.log(weak_Set.has(settings.now_playing));//false

In the sample code above when the reference to 'setting;now_playing' is deleted the value inside 'weak_Set' also gets garbage collected. 

Sunday, 9 July 2017

Immutables in JavaScript

One of the most crucial features of a programming language is immutability of object types. Immutability helps the developers to maintain application state with certainty. Since for most applications immutable objects are used for constraint definition. 

In ECMAScript-5 all the primitive object types are immutable which are 'string', 'number', 'undefined', 'boolean' and 'null'. Additionally in ECMAScript-6 there is a new primitive type called 'Symbol' which is also immutable.

The 'object' type is mutable in JavaScript and this can lead to unintended code behavior if preventive measures are not taken. The three most commonly used 'object' types in JavaScript are arrays, object literals and functions. 

Arrays:


let arra = [];
let arrb = arra.push('test_number_1');

console.log(arra);//['test_number_1']
console.log(arrb);//['test_number_1']

For the above code the objective to push 'test_number_1'  to only 'arrb' will not be achieved. This is because setting 'arrb' to modified version of 'arra' first alters 'arra' itself and then assigns the reference to 'arrb'. So 'arra' is the reference to the array object. 

To solve this issue first a new instance of 'arra' needs to be created and then 'test_number_1' will be pushed to that array and 'arra' will remain empty.

let arra= [];

//solution EcmaScript-5
let arrb = arra.slice();
    arrb.push('test_number_1');

console.log(arra);//[]
console.log(arrb);//['test_number_1']

//solution EcmaScript-6
arrb = Array.from(arra);
arrb.push('test_number_1');

console.log(arra);//[]
console.log(arrb);//['test_number_1']

Object Literals:


let object_a = {};
let object_b = object_a;
    object_b.status = 1;


console.log(object_a);//{status: 1}
console.log(object_b);//{status: 1}

In the sample code above it can be observed that changing the property of 'object_b' inadvertently changes the property of 'object_a'. This is because when 'object_a' is set to 'object_b' it has the reference to the original object and changing properties 'object_b' will then change the property of the reference object('object_a').

//solution1 in Ecmascript-5
var object_a={'status':5};
var object_b = {}; 

for(var property in object_a){
    if(object_a.hasOwnProperty(property)){
        object_b[property] = object_a[property];
    }
}

object_b.status = 1;

console.log(object_a);//{status: 5}
console.log(object_b);//{status: 1}

The above solution-1 works best with shallow object literals which are one level deep.  First a copy of 'object_a' is created then the property 'status' is changed. In this case the original object 'object_a' remains the same and only 'object_b' affected.


//solution-2 in Ecmascript-5
var object_a=Object.freeze({'status':5});
var object_b = object_a; 

object_b.status = 1;

console.log(object_a);//{status: 5}
console.log(object_b);//{status: 5}

The solution-2 implemented in the above code actually makes 'object_a' immutable using 'Object.freeze()' method. This way any accidental change of 'object_a' can be prevented.But it should be noted that 'Object.freeze()' method does not do deep freezing and this solution is best suited for shallow objects.


//Solution ECMAscript-6
let object_a={'status':5};
let object_b = Object.assign({}, object_a, {'status': 1});

object_b.status = 1;

console.log(object_a);//{status: 5}
console.log(object_b);//{status: 1}

ECMAScript-6 has a more elegant solution built into the language. Using 'Object.assign' method the old object can be assigned to a new object and any updates can also be passed as arguments. This way the 'object_b' doesn't have any reference to 'object_a' so any unintended behavior is avoided.

'const' in ECMAScript-6:

Variables that are declared as constants in EcmaScript-6 aren't actually immutable. The 'const' keyword is used to prevent re-binding of variable.

//Object literals
const settings = {'value': 1, 'color': 'blue'};
      
      //unaltered object
      console.log(settings);//{value: 1, color: 'blue'}

      settings.value = 5;
      settings.color = 'red';

      //altered object
      console.log(settings);//{value: 4, color: 'red'}


//Arrays
const info_arry = [1, 4, 4, 6, 234, 4, 1];
      
      //unaltered object
      console.log(info_arry);//[1, 4, 4, 6, 234, 4, 1]

      info_arry[0] = 5;
      info_arry[2] = 256;

      //altered object
      console.log(info_arry);//[5, 4, 256, 6, 234, 4, 1]

In the above example it is evident that the 'const' keyword does not make arrays or object literals immutable. So the 'const' keyword should be used with the right intention that is to prevent variable rebinding.