JavaScript call stack

In JavaScript, function calls form a stack of frames. Consider the following example:

    function c(z2) { 
        console.log(new Error().stack); 
    } 
    function b(z1) { 
        c(z1+ 1); 
    } 
    function a(z) { 
        b(z + 1); 
    } 
    a(1);  
 
    //at c (eval at <anonymous>) 
    //at b (eval at <anonymous>) 
    //at a (eval at <anonymous>) 

When we call function a(), the first frame in the stack is created with arguments to the function and all local variables in the a()function. When function a() calls function b(), a second frame is created and pushed to the top of the stack. This goes on for all function calls. When the c()function returns, the top frame from the stack is popped out, leaving functions b() and a(); this goes on until the entire stack is empty. This is necessary to maintain because once the function finishes execution, JavaScript will need to know where to return.

Message queue

The JavaScript runtime contains a message queue. This queue contains the list of messages to be processed. These messages are queued in response to events such as click or an HTTP response received. Each message is associated with a callback function.

Event loop

A browser tab runs in a single thread-an event loop. This loop continuously picks messages from the message queue and executes the callbacks associated with them. The event loop simply keeps picking tasks from the message queues while other processes add tasks to the message queue. Other processes such as timers and event handlers run in parallel and keep adding tasks to the queue.

Timers

The setTimeout()method creates a timer and waits until it fires. When the timer is executed, a task is added to the message queue. The setTimeOut() method takes two arguments: a callback, and the duration in milliseconds. After the duration, the callback is added to the message queue. Once the callback is added to the message queue, the event loop will eventually pick it up and execute it. There is, however, no guarantee when the callback will be picked up by the event loop.

Run to completion

When the event loop picks up a message from the queue, the associated callback is run to completion. This means that a message is processed completely before the next message from the queue is processed. This property gives the asynchronous model a sense of predictability. As there is no intervention to preempt any of the messages in between execution, this model is much simpler than other models, where any unit of execution can be halted in between. However, once the message is picked up, even if the execution takes too long, any other interaction on the browser is blocked.

Events

You can register event handlers for an object and receive results of a method asynchronously. The following example shows how we can set up event handlers for the XMLHttpRequest API:

    var xhr = new XMLHttpRequest(); 
    xhr.open('GET', 'http://babeljs.io', true); 
    xhr.onload = function(e) { 
      if (this.status == 200) { 
        console.log("Works"); 
      } 
    }; 
    xhr.send(); 

In the preceding snippet, we are creating the object of the XMLHttpRequest class. Once the request object is created, we will register event handlers for it. Event handlers, such as onload(), are triggered asynchronously when the response is received from the open() method.

The send() method doesn't actually initiate the request, it adds the request to the message queue for the event loop to pick it up and execute necessary callbacks associated with it.

Callbacks

The Node.js application popularized this style of receiving asynchronous data. A callback is a function passed as the last argument to the asynchronous function call.

To illustrate the usage, let's use the following example of reading a file in Node.js:

    fs.readFile('/etc/passwd', (err, data) => { 
      if (err) throw err; 
     console.log(data); 
    }); 

Don't worry about a few details here. We are using the filesystem module as an fs alias. This module has a readFile method to read a file asynchronously. We will pass the file path and filename as the first argument and a callback function as the last argument of the function. We are using an anonymous function as the callback in the example.

The callback function has two arguments-error and data. When the readFile() method is successful, the callback function receives data, and if it fails, the error argument will have the error details.

We can also use a slightly functional style to write the same callback. Consider the following example:

    fs.readFile('/etc/passwd',  
      //success 
      function(data) { 
        console.log(data) 
      }, 
      //error 
      function(error) { 
        console.log(error) 
      } 
    );   

This style of passing callbacks is also called continuous-passing style (CPS); the next step of execution or continuation is passed as a parameter. The following example further illustrates the CPS style of callbacks:

    console.log("1"); 
    cps("2", function cps_step2(val2){ 
      console.log(val2); 
      cps("3", function cos_step3(val3){ 
        console.log(val3); 
      }) 
      console.log("4"); 
    }); 
    console.log("5"); 
    //1 5 2 4 3 
 
    function cps(val, callback) { 
      setTimeout(function () { 
            callback(val); 
      }, 0); 
    } 

We will provide the continuation (the next callback) to each step. This nested callback style also causes a problem sometimes referred to as callback hell.

Callbacks and the CPS introduce a radically different style of programming. Although it is easier to understand callbacks compared to other constructs, callbacks can create slightly difficult to understand code.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.137.178.133