Q1. How you will create Pub-Sub design pattern in Javascript, What are the benefits of using it?

A. publish-subscribe is a messaging pattern where senders of messages, called publishers, do not program the messages to be sent directly to specific receivers, called subscribers, but instead categorize published messages into classes without knowledge of which subscribers if any, there may be. Similarly, subscribers express interest in one or more classes and only receive messages that are of interest, without knowledge of which publishers, if any, there are.

Here is how we can create a Pub-Sub Pattern in Javascript.

var pubSub = {};
(function(q) {

  var messages = [];

  q.c = function(message, fn) {
    if (!messages[message]) {
      messages[message] = [];
    }
    messages[message].push(fn);
  }

  q.publish = function(message) {
    /* fetch all the subscribers and execute*/
    if (!messages[message]) {
      return false;
    } else {
      for (var message in messages) {
        for (var idx = 0; idx < messages[message].length; idx++) {
          if (messages[message][idx])
            messages[message][idx]();
        }
      }
    }
  }
})(pubSub);

So here we created pubSub object with subscribe and publish methods in it, now we can use it for any event we will publish, all subscribe callback will get executed once they receive published event.

Here is code:

pubSub.subscribe("event-A", function() {
  console.log('this is A');
});

pubSub.subscribe("event-A", function() {
  console.log('booyeah A');
});

pubSub.publish("event-A"); 

Benefits of using PubSub:

  • Loose coupling
  • Scalability

Q. Given the list of images, How you will lazy load images on browser scroll?

To solve this issue we will use IntersectionObserver API which browsers recently added. The Intersection Observer API provides a way to asynchronously observe changes in the intersection of a target element with an ancestor element or with a top-level document’s viewport.

For the given images we will use IntersectionObserver to make it lazy-loaded on browsers.

<img class='img' src="https://images.unsplash.com/photo-1579941001676-c02c52d4155b?ixlib=rb-1.2.1&q=8&fm=jpg&crop=entropy&cs=tinysrgb&w=400&fit=max&ixid=eyJhcHBfaWQiOjE0NTg5fQ" data-src="https://images.unsplash.com/photo-1579941001676-c02c52d4155b?ixlib=rb-1.2.1&q=85&fm=jpg&crop=entropy&cs=tinysrgb&w=400&fit=max&ixid=eyJhcHBfaWQiOjE0NTg5fQ"><br>
<img class='img' src="https://images.unsplash.com/photo-1579941001676-c02c52d4155b?ixlib=rb-1.2.1&q=8&fm=jpg&crop=entropy&cs=tinysrgb&w=400&fit=max&ixid=eyJhcHBfaWQiOjE0NTg5fQ" data-src="https://images.unsplash.com/photo-1579941001676-c02c52d4155b?ixlib=rb-1.2.1&q=85&fm=jpg&crop=entropy&cs=tinysrgb&w=400&fit=max&ixid=eyJhcHBfaWQiOjE0NTg5fQ"><br>
<img class='img' src="https://images.unsplash.com/photo-1579941001676-c02c52d4155b?ixlib=rb-1.2.1&q=8&fm=jpg&crop=entropy&cs=tinysrgb&w=400&fit=max&ixid=eyJhcHBfaWQiOjE0NTg5fQ" data-src="https://images.unsplash.com/photo-1579941001676-c02c52d4155b?ixlib=rb-1.2.1&q=85&fm=jpg&crop=entropy&cs=tinysrgb&w=400&fit=max&ixid=eyJhcHBfaWQiOjE0NTg5fQ"><br>
<img class='img' src="https://images.unsplash.com/photo-1579941001676-c02c52d4155b?ixlib=rb-1.2.1&q=8&fm=jpg&crop=entropy&cs=tinysrgb&w=400&fit=max&ixid=eyJhcHBfaWQiOjE0NTg5fQ" data-src="https://images.unsplash.com/photo-1579941001676-c02c52d4155b?ixlib=rb-1.2.1&q=85&fm=jpg&crop=entropy&cs=tinysrgb&w=400&fit=max&ixid=eyJhcHBfaWQiOjE0NTg5fQ"><br>
<img class='img' src="https://images.unsplash.com/photo-1579941001676-c02c52d4155b?ixlib=rb-1.2.1&q=8&fm=jpg&crop=entropy&cs=tinysrgb&w=400&fit=max&ixid=eyJhcHBfaWQiOjE0NTg5fQ" data-src="https://images.unsplash.com/photo-1579941001676-c02c52d4155b?ixlib=rb-1.2.1&q=85&fm=jpg&crop=entropy&cs=tinysrgb&w=400&fit=max&ixid=eyJhcHBfaWQiOjE0NTg5fQ"><br>

First, we will check it IntersectionObserver is present or not. If it is there we will write options and callbacks to use it.

if(!!window.IntersectionObserver){
  const options = {
    root: null,
    rootMargin: '-200px',
    threshold: 0.5
  };
  
  const callback = (entries, observer) => {
    entries.forEach(entry => {
      if(entry.isIntersecting){
        entry.target.src = entry.target.dataset.src;
        observer.unobserve(entry.target);
      }
    });
  }
 const observer = new IntersectionObserver(callback, options);
} else {
  console.log('bye!')
}

Now we can add our target which we want to observe –

 document.querySelectorAll('img').forEach(img => { 
    console.log('img');
    observer.observe(img) 
  });

Q. What is the Order of Execution of following async functions and why?

function wrapper() {
  setTimeout(() => {
    console.log("setTimeout1");
  });
  new Promise(resolve => {
    console.log("executor function");
    setTimeout(() => {
      console.log("setTimeout2");
    });
    resolve("result");
  }).then(result => {
    console.log(result);
  });
  console.log("line after promise chain");
}


function init() {
  console.log('init start');
  wrapper();
  console.log('init end here');
}

init();

Order –

  • init start
  • executor function
  • line after promise chain
  • init end here
  • result
  • setTimeout1
  • setTimeout2

Why?

Javascript Event loop first execute the sync operations which are in stack, so upon calling init(), it will print ‘init start’ first then it will call the wrapper function, now in wrapper function, it will start executing sync operations so it will print ‘executor function’, now interesting fact here is whatever callback you send to the Promise, it executes it synchronously. After that, it will print ‘line after promise chain’ and ends wrapper function execution and print ‘init end here’. Now all sync operations are completed so the javascript event loop will pick microtasks first before executing Macro tasks (tasks in the queue). Promise resolution is a microtask to it will print ‘result’. after that, it will start executing task in the queue so it will print ‘setTimeout1’ and ‘setTimeout2’.

Q. Create a basic implementation of a streams API. The user should be able to push values to a stream, and subscribe to values that are pushed to that stream.

const z = new Stream();
z.subscribe((value) => console.log(value));
z.subscribe((value) => console.log(value * 2));
z.subscribe((value) => console.log(value * 3));
z.push(2);

Should return
2
4
6

A. We can do it by using ES6 class Stream, and provide it two methods subscribe and push. We will maintain subscriber callback list in variable and upon exection of push method we will execute all those callbacks.

Here is sample code for the same –

class Stream {
  constructor() {
    this.allSubscribe = [];
  }
  
  subscribe = (fn) => {
    this.allSubscribe.push(fn);
  }
  
  push = (value) => {
    this.allSubscribe.map( sub => {
      sub.call(null, value);
    })
  }
  
}

const z = new Stream();
z.subscribe((value) => console.log(value));
z.subscribe((value) => console.log(value * 2));
z.subscribe((value) => console.log(value * 3));
z.push(2);

Q. Create a horizontal progress bar in HTML, CSS, and Javascript?

Here we can use simple div to create a progress bar.

<div id='loader'></div>

Now we will add some CSS to make it look like a progress bar –

#loader {
  height: 20px;
  background: red;
  width: 0;
}

Now we need to show the progress based on the data loading. So initially we will assume loading is 0 and keep the width of the progress bar as 0, now as soon as loading will get increase we will calculate it corresponding to our container width and increase the width.

(function() {
  const totalTime = 3000;
  const totalWidth = 100;
  let initialWidth  = 1;
  const loader = document.getElementById('loader');
  function progress () {
      const inter = setInterval(function(){
      if(initialWidth > 100) {
        clearInterval(inter);
      } else {
        initialWidth++;
        loader.style.width = initialWidth + '%'; 
      } 
    },totalTime/totalWidth);
  }
})();

That’s all, Let me know if you have any queries in the comment box.