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
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)
]);