Web Development with Node.js

LV 02 - Functions, Closures, Callbacks, Promises ...

Daniel Khan / daniel@khan.io / University of Applied Sciences Hagenberg / KWM

In JavaScript functions are
First
Class
Citizens

First Class Citizens ...
... can be assigned to a variable

function adderFunction(a, b) {
  return a + b;
};

const add = adderFunction;

const addResult = add(33, 9);

console.log(addResult);

First Class Citizens ...
... can be function parameters

function doMath(mathFunc, a, b) {
  return mathFunc(a, b);
}

function add(a, b) {
  return a + b;
};

function multiply(a, b) {
  return a * b;
};


const addResult = doMath(add, 33, 9);
console.log(addResult);

const multiplyResult = doMath(multiply, 4, 7);
console.log(multiplyResult);


// Question: What will this log to the console?
const addResult2 = doMath(add(), 12, 4);
console.log(addResult2);

First Class Citizens ...
... can be returned from a function

function add(a, b) {
  return a + b;
};

function multiply(a, b) {
  return a * b;
};

function giveMe(functionType) {
  if(functionType === 'add') return add;
  if(functionType === 'multiply') return multiply;

  throw new Error('Unknown Type');  
}

const myAdder = giveMe('add');

console.log(myAdder(4, 2));


Real Quick: Pass by Value/Reference

function countUp(a) {
    a += 1;
}

const a = 5;
console.log(a);

countUp(a);
console.log(a);

//////////////////////////////////////

function changeName(person, newName) {
    person.lastName = newName;
}

const tim = {
    firstName:'Tim',
    lastName: 'Smith',
};

console.log(tim);

changeName(tim, 'Miller');

console.log(tim);

Closures

  • Closures are functions that refer to independent (free) variables (variables that are used locally but defined in an enclosing scope)
  • In other words, these functions 'remember' the environment in which they were created

Example: Closure Counter

function createCounter() {
  let count = 0;
  return function () {
    return count++;
  }
}

const count = createCounter();

console.log(count());
console.log(count());
console.log(count());

Example: Closure Adder

function makeAdder(x) {
  return function (y) {
    return x + y;
  }
}

const add5 = makeAdder(5);
const add7 = makeAdder(7);

console.log(add5(3));
console.log(add7(3));

Exercise: sayTo Function

const say = sayTo('John');
console.log(say('it will rain today!'));

Implement a function sayTo()’ that takes a name like 'John' as an argument and returns a function that takes a phrase like 'it will rain tomorrow'.

The returned function should return a string like 'John, it will rain tomorrow'.

Callbacks ..

... are just functions that are passed to another function and called in the end (most probably with the result). 

function resultLogger(r) {
    console.log(`The result is ${r}`);
}

function adder(a, b, callback) {
    const result = a + b;
    return callback(result);
}

adder(7, 9, resultLogger);

Callbacks ..

... are popular in JavaScript. Think of event handlers.

someButton.onclick = function(){alert('You clicked')};

Event handlers are callbacks that are executed when an event happens.

Callbacks ..

... are in Node.js a way to deal with things that happen asynchronously

function filePrinter(err, fileContent) {
    if(err)
        console.error('Oh no!');
    else
        console.log(`The content of the file is ${fileContent}`);
}

fs.readFile('test.txt', 'utf8', filePrinter);

Behind the Scenes

fs.readFile('test.txt', 'utf8', callback);
callback(err, result);

System Call

Callback Hell ...

Promises ...

const readFilePromise = function(file, encoding) {
  return new Promise(function(ok, notOk) {
    fs.readFile(file, encoding, function(reject, resolve) {
        if (err) {
          reject(err)
        } else {
          resolve(data)
        }
    })
  })
}

function filePrinter(fileContent) {
    console.log(`The content of the file is ${fileContent}`);
}


readFilePromise('test.txt', 'utf8').then((fileContents) => {
    console.log(`The content of the file is ${fileContent}`);
})
.catch((err) => {
    console.error(err);
);

// readFilePromise('test.txt', 'utf8').then(filePrinter).catch(err => console.error(err));

... are another way to deal with asynchronous operations

Promises ...

... are a unit of work that get's either resolved or rejected eventually

async functions

... will always return a promise

async function asyncAdd(a, b) {
  return a + b;
}

const addPrms = asyncAdd(3, 7);

console.log(addPrms);

addPrms.then((res) => {
  console.log(`Then it was ${res}`);
});

async functions

... can be await-ed

async function asyncAdd(a, b) {
  return a + b;
}


(async function doIt() {
  const result = await asyncAdd(3, 7);
  console.log(`I waited for ${result}`);
})();

... inside another async function

async/await

const readFilePromise = function(file, encoding) {
  return new Promise(function(ok, notOk) {
    fs.readFile(file, encoding, function(reject, resolve) {
        if (err) {
          reject(err)
        } else {
          resolve(data)
        }
    })
  })
}

function filePrinter(fileContent) {
    console.log(`The content of the file is ${fileContent}`);
}


async function doReadFile(file) {
    const readFileResult = await readFilePromise(file, 'utf8');
    filePrinter(readFileResult);
    return `${file} was read`;
}



doReadFile('test.txt')
.then(result => console.log(result));
.catch((err) => console.error(err));

... makes code easier to read

async/await

... may lead to serial executions

const issPosition = await getIssLocation();
const crewMembers = await getIssCrew();

const imagery = await getIssImageryUrl(issPosition.latitude, issPosition.longitude);
const rGeoCode = await reverseGeocode(issPosition.latitude, issPosition.longitude);
const [issPosition, crewMembers] = await Promise.all([getIssLocation(), getIssCrew()]);

const [imagery, rGeoCode] = await Promise.all([getIssImageryUrl(
  issPosition.latitude,
  issPosition.longitude),
  reverseGeocode(issPosition.latitude, issPosition.longitude)
]);
Made with Slides.com