Dr. Greg Bernstein
Updated March 18th, 2021
Example code: AsyncExamples.zip
Concurrency model and Event Loop Read this carefully
setTimeout() function. Proper definition of this function which gives direct access to the event queue.
The Node.js Event Loop. Good Information. Not required reading.
From MDN:
Stack: Memory used for function calls. Stack frames consist of function arguments and local variables.
Heap: Memory used for all variables and objects.
Message/Event Queue: Mechanism for JavaScript concurrency.
Processes: Implemented by an OS, processes get separate protected memory spaces. Highest in overhead.
Threads: Implemented by runtime, OS, or library. Threads get separate program counters. Memory can be shared between threads. Not easy to program with in general.
Review Threads and Processes
From MDN:
“A JavaScript runtime uses a message queue, which is a list of messages to be processed. Each message has an associated function which gets called in order to handle the message.”
Psuedo-code model from MDN:
while (queue.waitForMessage()) {
queue.processNextMessage();
}
The previous code actually needs thread-like functionality to work.
The queue is ordered structure like a “waiting line”
Processing an event/message involves calling the callback function with parameters corresponding to the event or message.
The callback function is “run-to-completion”. This is known as non-preemptive scheduling. Review scheduling in OS
From MDN:
“In web browsers, messages are added anytime an event occurs and there is an event listener attached to it. If there is no listener, the event is lost.”
Using the setTimeout() function
var timeoutID = scope.setTimeout(callback[, delay, param1, param2, ...]);
Modified from MDN:
console.log('this is the start');
setTimeout(function cb1() {
console.log('this is a msg from call back 1');
});
console.log('this is just a message');
setTimeout(function cb2() {
console.log('this is a msg from call back 2');
}, 0);
console.log('this is the end');
badTimes.js
What will this do?
myTime = 0.0;
startTime = new Date();
function advanceTime() {
myTime += 1.0;
elapsedTime = (new Date() - startTime)/1000.0;
console.log(`myTime = ${myTime}, elapsedTime = ${elapsedTime}`);
}
// What will this do?
setTimeout(advanceTime, 1000);
setTimeout(advanceTime, 1000);
setTimeout(advanceTime, 1000);
setTimeout(advanceTime, 1000);
Not really a good clock by any measure…
$ node badTimes.js
myTime = 1, elapsedTime = 1.001
myTime = 2, elapsedTime = 1.003
myTime = 3, elapsedTime = 1.003
myTime = 4, elapsedTime = 1.004
goodTimes.js
: Nest those calls!
myTime = 0.0;
startTime = new Date();
// What will this do?
setTimeout(function(){
myTime += 1.0;
elapsedTime = (new Date() - startTime)/1000.0;
console.log(`myTime = ${myTime}, elapsedTime = ${elapsedTime}`);
setTimeout(function(){
myTime += 1.0;
elapsedTime = (new Date() - startTime)/1000.0;
console.log(`myTime = ${myTime}, elapsedTime = ${elapsedTime}`);
setTimeout(function(){
myTime += 1.0;
elapsedTime = (new Date() - startTime)/1000.0;
console.log(`myTime = ${myTime}, elapsedTime = ${elapsedTime}`);
setTimeout(function(){
myTime += 1.0;
elapsedTime = (new Date() - startTime)/1000.0;
console.log(`myTime = ${myTime}, elapsedTime = ${elapsedTime}`);
}, 1000);
}, 1000);
}, 1000);
}, 1000);
It counts! But not pretty
$ node goodTimes.js
myTime = 1, elapsedTime = 1.001
myTime = 2, elapsedTime = 2.005
myTime = 3, elapsedTime = 3.009
myTime = 4, elapsedTime = 4.01
From goodTimesMore.js
// Find the "pyramid of doom"
let myTime = 0.0;
let startTime = new Date();
function updateTime() {
myTime += 1.0;
elapsedTime = (new Date() - startTime) / 1000.0;
console.log(`myTime = ${myTime}, elapsedTime = ${elapsedTime}`);
}
// What will this do?
setTimeout(function() {
updateTime();
setTimeout(function() {
updateTime();
setTimeout(function() {
updateTime();
setTimeout(function() {
updateTime();
setTimeout(function() {
updateTime();
setTimeout(function() {
updateTime();
setTimeout(function() {
updateTime();
setTimeout(function() {
updateTime();
}, 1000);
}, 1000);
}, 1000);
}, 1000);
}, 1000);
}, 1000);
}, 1000);
}, 1000);
Ugly deeply nested code! And we didn’t deal with error conditions. When this gets deeper and more complicated it is known as “callback hell” or the “pyramid of doom”.
Alternative Approach: JavaScript Promises