Asynchronous programming in Javascript

Start of the discussion

  • the Javascript engine is single-threaded (every javascript code gets executed on the main thread)
  • can we do parallel tasks then ?
  • yes, because the browser has several threads

Async programming

  • asynchronous programming is a form of parallel programming in which a task is executed on a separate thread and when it completes it notifies the main thread (which can execute other things meanwhile)
  • synchronous programming is the sequential programming where while a task is executed, any other code must wait for the task to finish before it can get executed

Event handling - the simplest form of async programming in JS


let elem = document.querySelector("#button");
elem.addEventListener("click", function() {
	var newDiv = document.createElement("div"); 
  	var newContent = document.createTextNode("Text.."); 
	newDiv.appendChild(newContent);
	document.body.appendChild(newDiv);  
});
		

window.setTimeout

  • 
    var timeoutID = window.setTimeout(func, delay, [param1, param2, ...])
    		
    Calls function "func" with the specified parameters after "delay" miliseconds have passed.
  • 
    window.clearTimeout(timeoutID);
    		
    Clears the timeout (async function is no longer called).

window.setInterval

  • 
    var intervalID = window.setInterval(func, [delay, param1, param2, ...]);
    		
    Calls function "func" repeatedly with the specified parameters after "delay" miliseconds have passed.
  • 
    window.clearInterval(intervalID);
    		
    Clears the timeout interval (async function is no longer called).

Javascript execution

Call Stack
Browser API (DOM, XHR, Fetch, etc.)

Task queue
Micro-task queue

Javascript execution (cont.)

  • Call stack: JavaScript runtime call stack. Executes the JS commands, functions.
  • Task Queue: When the browser internal implementation notices a callback from something like setTimeout or addEventListener should be fired, it creates a Task and enqueues it in the Task Queue
  • Micro-task Queue: Promises are special tasks that execute with higher priority than normal tasks, so they have their own special queue
  • Event loop: Processes the task queues.
    • When the call stack is empty, the event loop pulls the next task from the task queues and puts it on the call stack.
    • The Micro-task queue has higher priority than the Task Queue.

Asynchronous programming (promises)


const myFirstPromise = new Promise(function (resolve, reject) {   	
        // do something asynch which eventually calls either: 
                // resolve(someValue); // fulfilled 
                // or 
                // reject("failure reason"); // rejected 
});
		
The executor function is executed immediately by the Promise implementation, passing resolve and reject functions (the executor is called before the Promise constructor even returns the created object); the resolve and reject functions are provided by the Javascript engine. The resolve and reject functions, when called, resolve or reject the promise, respectively. The executor normally initiates some asynchronous work, and then, once that completes, either calls the resolve function to resolve the promise or else rejects it if an error occurred. If an error is thrown in the executor function, the promise is rejected. The return value of the executor is ignored.

Getting the returned result from a Promise

  • Promises can not return anything, they just pass results to callbacks (which are specified with .then())
  • Without .then() you can not get the returned result from the Promise!

const p = new Promise(function(resolve,reject) {
	// the asynchronous work - just a console.log()
	console.log("Doing some work...");
	resolve("Done");
});

// we assume the Promise p is always resolved
p.then(function(result) { 
	console.log(result); // just print the returned result of the Promise
})
			

Async and await

  • async placed in front of a function makes that function return a promise
    
    async function f(arg) {
    	console.log(arg);
    }
    f().then(alert);
    				
  • await can be placed in front of any async promise-based function to pause your code on that line until the promise fulfills, then return the resulting value;
    
    async function fetchImage() {
    	let img = await fetch('/img/a.jpg');
    	...
    }
    				
  • await only works in async functions

Web Workers

  • allow running js code in the background threads of the browser
  • a worker communicates with the code that created it through message passing

var worker = new Worker('doWork.js');

worker.addEventListener('message', function(e) {
  console.log('Worker said: ', e.data);
}, false);

worker.postMessage('Hello World'); // Send data to our worker.