@adamterlson
adam.terlson@gmail.com
Now even awesomer
function entireWorkflow(callback) {
fs.readdir('/some/path', function (err, fileNames) {
// Often forgotten or ignored is handling err
if (err) throw new Error("We're screwed");
fileNames.forEach(function (name) {
fs.readFile(name, function (fileErr, file) {
// Especially when doing nested or more complex flows
if (fileErr) throw new Error("This is getting seriously annoying");
// Insert value-adding code here
});
});
// Uhh... when do I call `callback()`?
});
}
function asyncStuff() {
var dfd = Q.defer();
dfd.resolve(10);
// or
dfd.reject(new Error('Reason'));
return dfd.promise;
}
Revealing constructor pattern:
new Promise((resolve, reject) => {
resolve(10);
// or
reject(new Error('Reason'));
});
p instanceof Promise;
p.constructor === Promise;
Object.getPrototypeOf(p) === Promise.prototype;
Now possible:
Promise.resolve('A')
.then(a => $.Deferred().resolve(a + 'B').promise())
.then(b => Q(b + 'C'))
.then(c => console.log(c)) // ABC
.catch(handleTheError)
window.crypto.subtle.generateKey(...)
.then(function(key){
console.log(key);
})
.catch(function(err){
console.error(err);
});
try {
let topPost = getTopRedditPost();
console.log("Top Post: ", topPost.title);
let topComment = getTopComment(topPost);
console.log("Top Comment: ", topComment.body);
}
catch(err) {
console.log("Error: " + err);
}
function GET(url, cb) {
var req = new XMLHttpRequest();
req.open('GET', url);
req.send();
req.onreadystatechange = function () {
if (req.readyState === 4) {
if (req.status === 200) {
cb(null, JSON.parse(req.responseText));
} else {
cb(new Error(`Failed Request with Status: ${req.status}`));
}
}
};
}
GET('https://www.reddit.com/r/javascript/top.json', function (err, posts) {
if (err) console.log(`Problem: ${err.message}`);
var topPost = posts.data.children[0].data;
console.log(`Top Post: ${topPost.title}`);
GET(`https://www.reddit.com/r/javascript/comments/${topPost.id}.json`, function (err, comments) {
if (err) console.log(`Problem: ${err.message}`);
var topComment = comments[1].data.children[0].data;
console.log(`Top Comment: ${topComment.body}`);
});
});
getTopRedditPost();
.then(topPost => {
console.log(`Top Post: ${topPost.title}`);
return getTopComment(topPost)
.then(topComment => {
console.log(`Top Comment: ${topComment.body}`);
});
})
.catch(err => console.log(`Error: ${err.message}`));
function getTopRedditPost() {
return GET('https://www.reddit.com/r/javascript/top.json')
.then(posts => posts.data.children[0].data);
}
function getTopComment(post) {
return GET(`https://www.reddit.com
/r/javascript/comments/${topPost.id}.json`)
.then(comments => comments[1].data.children[0].data);
}
try {
var topPost = getTopRedditPost();
console.log("Top Post: ", topPost.title);
var topComment = getTopComment(topPost);
console.log("Top Comment: ", topComment.body);
}
catch(err) {
console.log("Error: " + err);
}
(if you weren't already)
let namePromises = [
GETPROFILE('sally'),
GETPROFILE('bob'),
GETPROFILE('sue')
];
Promise.all(names)
.then(profiles => console.log(profiles.length)); // 3
// OR written more realistically with Map
let names = ['sally', 'bob', 'sue'];
Promise.all(names.map(name => GETPROFILE(name)))
.then(profiles => console.log(profiles.length)); // 3
try {
let top5Posts = getTop5RedditPosts();
for (let post of top5Posts) {
console.log('Top Post:', post.title);
let topComment = getTopComment(post);
console.log('Top Comment:', topComment.body);
}
}
catch(err) {
console.log('Error:', err);
}
getTop5RedditPosts()
.then(topFivePosts => {
return Promise.all(topFivePosts.map(post => {
return getPostTopComment(post)
.then(topComment => {
console.log(`----\nTop Post: ${post.data.title}`);
console.log(`Top Comment: ${topComment.body}`);
});
}));
})
.catch(err => console.log(`Error: ${err.message}`));
function getTop5RedditPosts() {
return GET('https://www.reddit.com/r/javascript/top.json')
.then(posts => posts.data.children.slice(0, 5));
}
function getTopComment(post) {
return GET(`https://www.reddit.com
/r/javascript/comments/${topPost.id}.json`)
.then(comments => comments[1].data.children[0].data);
}
try {
let top5Posts = getTop5RedditPosts();
for (let post of top5Posts) {
console.log('Top Post:', topPost.title);
let topComment = getTopComment(topPost);
console.log('Top Comment:', topComment.body);
}
}
catch(err) {
console.log('Error:', err);
}
iterface IIterable {
[Symbol.iterator](): IIterator
}
interface IIterator {
next(): IIteratorResult,
throw()?: IIteratorResult
return()?: IIteratorResult
}
interface IIteratorResult {
value: T,
done: boolean
}
let arr = ['a', 'b'];
for (let pair of arr.entries()) {
console.log(pair); // [0, 'a'] [1, 'b']
}
let arr = ['a', 'b', 'c'];
let itr = arr.entries();
itr.next(); // {"value":[0,"a"],"done":false}
itr.next(); // {"value":[1,"b"],"done":false}
itr.next(); // {"value":[2,"c"],"done":false}
itr.next(); // {"done":true}
let msg = "hi";
let iterator = msg[Symbol.iterator]();
iterator.next(); // { value: "h", done: false }
iterator.next(); // { value: "i", done: false }
iterator.next(); // { value: undefined, done: true }
Use the function* syntax
function* producer() {
yield 1;
for (let i = 2; i <= 4; i++) {
yield i;
}
return 5;
}
var iter = producer();
Array.from(iter); // ???
var iter = producer();
iter.next() // { done: false, value: 1 }
iter.next() // { done: false, value: 2 }
iter.next() // { done: false, value: 3 }
iter.next() // { done: false, value: 4 }
iter.next() // { done: true, value: 5 }
var iter = producer();
Array.from(iter); // [1,2,3,4] WAT - Iteration doesn't include return
Yield pauses, returns yielded value to the consumer
function* producer() {
yield 1;
}
producer().next(); // { value: 1, done: false }
Next() continues execution and takes an optional value
function* producer() {
let a = yield 1;
return a + 1;
}
let p = producer();
p.next(); // { value: 1, done: false }
p.next(2); // { value: 3, done: true }
var producer = function*() {
let a = yield 10;
console.log('A', a);
let b = yield a;
console.log('B', b);
return 30;
}
var iter = producer();
console.log('yielded', iter.next());
console.log('yielded', iter.next());
console.log('yielded', iter.next(100));
console.log('yielded', iter.next(200));
yielded {"value":10,"done":false}
A Undefined
yielded {"done":false}
B 100
yielded {"value":30,"done":true}
yielded {"done":true}
Spawn takes a generator function. Every time the generator yields a promise, the execution of the generator is halted until the promise is resolved at which time spawn will call next() with the promise's resolved value, on the generator.
function spawn(gen) {
let iter = gen();
let p1 = iter.next().value;
let p2 = p1.then(res => iter.next(res).value);
return p2;
}
spawn(function*(){
let a = yield Promise.resolve(10);
return a + 20;
})
.then(res => console.log(res)); // 30
Rework our same scenario to use an iterator function, yield, and spawn.
(spawn(function*(){
try {
let posts = yield GET('https://www.reddit.com/r/javascript/top.json');
let topFivePosts = posts.data.children.slice(0, 5);
for (let post of topFivePosts) {
let comments = yield GET(`https://www.reddit.com/r/javascript/comments/${post.data.id}.json`);
let topComment = comments[1].data.children[0].data;
console.log(`----\nTop Post: ${post.data.title}`);
console.log(`Top Comment: ${topComment.body}`);
}
} catch (ex) {
console.log(`Error: ${ex.message}`);
}
}))();
Q - .spawn()
Bluebird - .coroutine()
let fetchTop5 = async function() {
try {
let top5Posts = await getTop5RedditPosts();
for (let post of topFivePosts) {
console.log(`----\nTop Post: ${post.data.title}`);
let comment = await getTopComment(post);
console.log(`Top Comment: ${topComment.body}`);
}
} catch (ex) {
console.log('Error:', err);
}
};
fetchTop5();
function getTop5RedditPosts() {
return GET('https://www.reddit.com/r/javascript/top.json')
.then(posts => posts.data.children.slice(0, 5));
}
function getTopComment(post) {
return GET(`https://www.reddit.com
/r/javascript/comments/${topPost.id}.json`)
.then(comments => comments[1].data.children[0].data);
}
let fetchTop5 = async function() {
let posts = await GET('https://www.reddit.com/r/javascript/top.json');
let topFivePosts = posts.data.children.slice(0, 5);
for (let post of topFivePosts) {
let comments = await GET(`https://www.reddit.com/r/javascript/comments/${post.data.id}.json`);
let topComment = comments[1].data.children[0].data;
console.log(`----\nTop Post: ${post.data.title}`);
console.log(`Top Comment: ${topComment.body}`);
}
return 'All done!'; // Here's that return value that was ignored
};
let fetching = fetchTop5()
.then(res => console.log(res));
Async functions return a promise.
The resolved value is the value returned.
(if you weren't already)
@adamterlson
adam.terlson@gmail.com