UMN Code-People, May 5 2016
David Naughton - UMN Libraries Web Development
function1();
function2();
function3();
"...several computations are executing during overlapping time periods—concurrently—instead of sequentially..." — Wikipedia: Concurrent computing
Many examples...
"I don't care when these execute!"
Reveal.addEventListener("event1" function1, false);
Reveal.addEventListener("event2" function2, false);
Reveal.addEventListener("event3" function3, false);
"Maybe you just don't understand functional programming."
X
const result1 = function1();
const result2 = function2(result1);
const result3 = function3(result2);
X
try {
const result1 = function1();
const result2 = function2(result1);
const result3 = function3(result2);
} catch(e) { }
Isn't the JavaScrip/node.js event loop single-threaded?
callback(result)
to event queue.We'll focus mostly on this one problem. An evolution of approaches...
>
function1(function (err, result1) {
(err ? console.log(err) :
function2(result1, function(err, result2) {
(err ? console.log(err) :
function3(result2, function(err, result3) {
(err ? console.log(err) :
// Do something with result3
);
});
);
});
);
});
The infamous "callback hell", or "pyramid of doom", or arrow anti-pattern.
A proxy for the pending result of an async function, to be fulfilled or rejected later.
While we give up control with callbacks...
asyncFunction(function pleaseCallThis(err, result) {
// ...but only once, and please let me know about any errors!
});
...we keep it with promises:
const promise = asyncFunction();
promise.then(
function fulfilled (result) {},
function rejected (err) {}
);
function1()
.then(function (result1) {
function2(result1);
})
.then(function (result2) {
function3(result2);
})
.then(function (result3) {})
.catch(err);
function task() {
const p1 = subtask1();
const p2 = subtask2();
const p3 = subtask3();
Promise.all([p1, p2, p3]).then(function(results){});
}
...but it requires some setup.
Generators are iterator factories, defining iterative algorithms with single functions that maintain their own state, which can be paused and resumed.
function *naturals() {
var integer = 1;
while(true) {
yield integer++;
}
}
const it = naturals();
console.log(it.next().value); // 1
console.log(it.next().value); // 2
console.log(it.next().value); // 3
function *lc() {
while(true) {
var word = yield null; // yield consumes input
console.log(word.toLowerCase());
}
}
const it = lc();
it.next(); // Value would be ignored, only executes up to yield.
it.next('Hello'); // hello
it.next('World'); // world
it.next(42); // TypeError!
function *lc() {
while(true) {
var word = yield null; // yield consumes input
console.log(word.toLowerCase());
}
}
const it = lc();
it.next();
try {
it.next(42);
} catch(e) { } // TypeError
function *lc() {
while(true) {
try {
var word = yield null; // yield consumes input
console.log(word.toLowerCase());
} catch (e) {} // Exceptions caught here.
}
}
const it = lc();
it.next();
try {
it.throw('Sending the generator an exception.');
} catch(e) { } // Never gets here.
What if we yield a promise from a generator?
function *gen() {
try { var result = yield asyncFunction(); }
catch (e) { console.error(e); }
}
var it = gen();
var p = it.next().value;
p.then(
function fulfilled(result) { },
function(err){ gen.throw(err); }
);
Bluebird.coroutine(function *() {
try {
// functions return promises:
const result1 = yield function1();
const result2 = yield function2(result1);
const result3 = yield function3(result2);
}
catch (e) { console.error(e); }
return result3;
});
"...computer program components that generalize subroutines for nonpreemptive multitasking, by allowing multiple entry points for suspending and resuming execution at certain locations." — — Wikipedia: Coroutine
Generators are already semi-coroutines.
""Subroutines are special cases of ... coroutines." — Donald Knuth, "The Art of Computer Programming"