Promises and why they are...
function add (x, y) {
return x + y;
}
add = function (x, y) {
return x + y;
};
add = (x, y) => x + y;
add = (x, y) => {
return x + y;
}
constant = x => x;
There Are Other Rules
If you wish to make an apple pie from scratch, you must first invent the universe.Carl Sagan
var x = ‘hello’;
setTimeout(() => {
x = ‘world’;
console.log(x);
}, 100);
console.log(x);
//hello
//world
C, Java, etc.
Courtesy of Mozilla
var x = ‘hello’;
setTimeout(() => {
x = ‘world’;
console.log(x);
}, 100);
console.log(x);
//hello
//world
var x = ‘hello’;
console.log(x); //hello
//wait 100ms
sleep(100);
x = ‘world’;
console.log(x); //world
Complicated, unpredictable, hard to reason about.
Continuation Passing Style
Bring Your Own Behavior
add = (x, y, callback) => callback(x + y);
add(1, 2, (result) => {
console.log(result);
});
//3
$.get(‘/my-url/’, (result) => {
$(‘body’).html(result);
});
array.map(x => x + 1)
.filter(x => x > 2);
x = 0;
iterate = () => x++;
iterate(); // 1
// x = 1
$.get(‘/url/’, (string) => {
string.toUpperCase(); // This is useless and not affecting state
});
$.get(‘/url/’, (string) => {
$(‘.msg’).text(string); // Something useful and affecting state
});
...decreases the complexity of code.
$.get(‘/sales-history/’, (salesJson) => {
var totalSales = 0;
for (var i = 0, l = salesJson.length; i < l; i++) {
totalSales += salesJson[i].price;
}
//inserts some number
$(‘#total-sales’).text(totalSales);
}, ‘json’);
Very one off
//Reusable functions
property = prop => obj => obj[prop];
sum = a => a.reduce(((acc, x) => acc + x;), 0);
totalSales = json => sum(json.map(property(‘price’)));
$.get(‘/sales-history/’, (salesJson) => {
//inserts some number
$(‘#total-sales’).text(totalSales(salesJson));
}, ‘json’);
$.get(‘url’, (data) => {
$.get(‘other-url’, data, (otherData) => {
$.get(‘another-url’, otherData, (anotherData) => {
$.get(‘more-url’, anotherData, (moreData) => {
//holy shit finally do something.
});
});
});
});
var state = [],
timer = false,
observe = (state, index) => (data) => state[index] = data,
collectUnfufilled = () => state.filter(x => x == undefined);
$.get(‘url’, observe(state, 0));
$.get(‘other-url’, observe(state, 1));
$.get(‘another-url’, observe(state, 2));
timer = setInterval(function () {
if (!collectUnfulfilled().length && state.length == 4) {
clearInterval(time);
//holy shit finally do something.
}
}, 10),
async.parallel([
resolver => {
$.get(‘url’, (data) => {
resolver(null, data);
});
},
resolver => {
$.get(‘other-url’, (otherData) => {
resolver(null, otherData);
});
},
resolver => {
$.get(‘another-url’, (anotherData) => {
resolver(null, anotherData);
});
}
], (errors, results) => /*do things*/);
salesJson = () => [{price: 12}, {price: 33}, ...];
dot = prop => obj => obj[prop];
sum = a => a.reduce(((acc, x) => acc + x;), 0);
totalSales = json => sum(json.map(dot(‘price’)));
$(‘#total-sales’).html(
totalSales(
salesJson()));
//inserts some number
$(‘#total-sales’).html(
totalSales(
$.get(‘/sales-history/’, ‘json’)));
//Error
A declaration or assurance that one will do a particular thing or that a particular thing will happen.Oxford English Dictionary
A promise represents the eventual value returned from the single completion of an operation.Common JS Spec
continuationFunction(x, callback);
promiseFunction(x).then(callback);
promise.then(done, fail, progress);
promise.done(callback);
promise.fail(callback);
promise.progress(callback);
promise.always(callback);
deferred.resolve(value); // fires done
deferred.reject(value); // fires fail
deferred.notify(value); // fires progress
deferred.state(); // returns current state: resolved|rejected|pending
deferred.promise(); // returns a promise object linked to the Deferred
promiseAdd = (x, y) => {
var d = $.Deferred();
setTimeout(function () {
d.resolve(x + y);
}, 100);
return d.promise();
}
promiseAdd(1, 2)
.then(result => console.log(result));
// logs 3 after 100ms
It is composable!
We'll get to that later.
promise = promiseFunction();
newPromise = promise.then(callback);
If the callback returns...
$.get(‘/sales-json/’)
.then(totalSales)
.then(total => {
$(‘#total-sales’).html(total);
});
promise = $.get(‘/url/’);
promise.then(doThisOnDone);
promise.then(doThisAtTheSameTime);
doSomething(promise);
$.get(‘url’)
.then($.get.bind(null, ‘other-url’))
.then($.get.bind(null, ‘another-url’))
.then($.get.bind(null, ‘more-url’))
.then((moreData) => {
// holy shit that is fluent
});
$.when(
$.get(‘url’),
$.get(‘other-url’),
$.get(‘another-url’),
$.get(‘more-url’)
).then(allData => {
var data = allData[0],
otherData = allData[1],
anotherData = allData[2],
moreData = allData[3];
//Wow!
});
$.when(
$.get(‘url’),
$.get(‘other-url’),
).then(data => $.when(
$.get(‘another-url’, data[0]),
$.get(‘more-url’, data[1])
)).then(finalData => {
//what elegant synchronization
});
$.when(‘foo’)
.then(partialComputation)
.then(moreCompuation)
.then(justALittleMore)
.then((data) => {
// Phew! I’m glad we didn’t lock the ui.
});
//Reusable Functions
var chunk = (size, a) => {
var chunked = [];
for (var i = 0; i < a.length; i += size) {
chunked.push(a.slice(i, i + size));
}
return chunked;
},
promiseGetIDs = (url, idArray) => $.get(url, {ids: idArray});
//One Off Implementation
var idList = [1, 2, 3, 4…],
promises = chunk(5, idList)
.map(promiseGetIDs.bind(null, ‘/big-computation/’));
$.when.apply(null, promises).then(data => {
//do something with all that data
});
promise.fail(callback);
promise.then(success, fail);
promise.then(callback1)
.then(callback2) // Errors that occur here
.then(callback3)
.then(callback4, errorHandler); // Bubble to here
try {
promise.then(callback1)
.then(callback2) // Error that occur here
.then(callback3)
.then(callback4, errorHandler); // Bubble to here
} catch (e) {
errorHandler(e);
}
promise.then(callback1)
.then(callback2) // Exception thrown
.then(callback3)
.then(callback4);
promise.then(func1)
.done(); // Not jQuery’s done, this terminates the chain
function () {
promise.then(func1)
.then(func2) //Error Thrown
.then(func3)
} //Promise gets garbage collected
//Error thrown
Not All Promises Are Alike
async = () => {
var def = when.defer();
setTimeout(() => {
//...does something
def.fulfill(x);
}, 10);
return def.promise();
}
async = () => {
var def = when.defer();
setTimeout(() => {
throw Error('Bloody hell!');
def.fulfill(x);
}, 10);
return def.promise();
}
async = () => new RSVP.Promise((fulfill, reject) => {
setTimeout(() => {
//do something
throw Error('Bloody hell!');
fulfill(x);
}, 10);
});
div = (x, y) => x / y;
describe("div function", () => {
it("divides small from large", () {
expects(div(10, 5)).toBe(2);
});
it("returns decimals for large from small", () {
expects(div(5, 10)).toBe(.5);
});
it("returns Infinity on division by 0", () {
expects(div(10, 0)).toBe(Infinity);
});
//...more test cases
});
describe("promise function", () => {
it("returns async", (done) {
promiseFunc().then(data => {
expects(data).toBe(/*...*/);
done();
});
});
});
describe("Test suite for sales calculation", function() {
salesMock = [{price: 45000}, ...];
it("Total sales returns sum of all sales", function() {
expect(totalSales(salesMock)).toBe(1000000);
});
});
Q.nfbind();
Q.nfcall();
Q.nfapply();
deferred.promisify();
promise = new Promise((resolve, reject) => {
resolve('value');
});
JavaScript Threads
var w = new Worker(‘my-script.js’);
w.onmessage = e => {
console.log(e.data);
};
w.onerror = e => {
//handle the error
};
w.postMessage(message);
A promise represents the eventual value returned from the single completion of an operation.
promiseWorker = (script, message) => {
var d = $.Deferred(),
w = new Worker(script);
w.onmessage = e => d.resolve(e.data);
w.onerror = e => d.reject(e);
d.always(() => w.terminate());
w.postMessage(message);
return d.promise();
}
//One use worker
promiseWorker('foo.js', ['data', 'set', 'for', 'worker'])
.then(data => console.log(data));