The document discusses asynchronous programming in JavaScript and how it has evolved over time. It covers callbacks, promises, generators, and the new async/await syntax. Callbacks were difficult to read and handle errors across steps. Promises provided a better model but still had ceremony and didn't play nicely with other constructs. Generators allowed writing async code that looked synchronous but required wrapping in a promise. Async/await builds on promises and generators by providing syntax that looks like synchronous code while still being asynchronous under the hood. It addresses issues with previous approaches but developers still need to handle errors and can overuse await. Async/await is available in modern browsers and makes asynchronous JavaScript much cleaner.
14. PATTERN: SEQUENTIAL DEPENDENT OPERATIONS
- Get the user from the database
- Get that user's best friend
- Get that best friend's profile picture
- Add a mustache to everybody in the picture
- Show the transformed picture on the page
15. PATTERN: SEQUENTIAL DEPENDENT OPERATIONS
function mustachify(userId, callback) {
loadUserFromDatabase(userId, function (err, user) {
if (err) callback(err);
else user.getBestFriend(function (err, friend) {
if (err) callback(err);
else friend.getBestPhoto(function (err, photo) {
if (err) callback(err);
else addMustache(photo, function (err, betterPhoto) {
if (err) callback(err);
else showPhotoToUser(user, betterPhoto, callback);
});
});
});
});
}
18. PROMISES RECAP
A promise is a representation of a (potentially) ongoing process.
▸ Pending
▸ Fulfilled
▸ Rejected
19. PROMISES RECAP
Define a new promise:
var p = new Promise(function (resolve, reject) {
// ... Do some asynchronous things
// Eventually call resolve or reject
});
Promise.resolve({ myResult: 123 });
Promise.reject(new Error("BAD THINGS"));
26. PATTERN: COMBINING SEQUENTIAL RESULTS
- Get the current user's data
- Load the timeline for that user
- Show the user's details & timeline on screen
28. PATTERN: ASYNC IN A LOOP
- For each task:
- Run the task to completion
- Add the result to the task results array
- Move on to the next task
- Return the array of task results
29. PATTERN: ASYNC IN A LOOP
function runTasks(tasks) {
var accumulatedPromise = Promise.resolve([]);
for (var task of tasks) {
accumulatedPromise.then(function (accumulatedResults) {
return task.run().then(function (result) {
return accumulatedResults.concat(result);
});
});
}
return accumulatedPromise;
}
30. PATTERN: CONDITIONAL ON ASYNC RESULT
- If the server is up and the user's login is valid:
- Save their data
- Show a nice message
- Otherwise:
- Show an error
31. PATTERN: CONDITIONAL ON ASYNC RESULT
isServerAvailable().then(function (serverIsAvailable) {
return serverIsAvailable && isAuthenticationValid();
}).then(function (canSave) {
if (canSave) {
return saveData().then(function () {
showMessage("Data saved");
});
} else {
showWarning("Couldn't save data");
}
});
39. function* generateEveryNumber() {
var n = 0;
while(true) {
yield n;
n += 1;
}
}
for (var number of generateEveryNumber()) {
if (number > 1000) break;
console.log(number); // 1, 2, 3 ... 1000
}
40. function* sumUpNumbers() {
var accumulator = (yield);
while (true) {
var nextAddition = (yield accumulator);
accumulator += nextAddition;
}
}
var sum = sumUpNumbers();
sum.next(); // Need an initial next(), to run to first yield.
sum.next(1); // { value: 1, done: false }
sum.next(5); // { value: 6, done: false }
42. PATTERN: CONDITIONAL ON ASYNC RESULT
- If the server is up and the user's login is valid:
- Save their data
- Show a nice message
- Otherwise:
- Show an error
43. PATTERN: CONDITIONAL ON ASYNC RESULT
spawn(function* () {
if ((yield isServerAccessible()) &&
(yield isAuthenticationValid())) {
yield saveData();
showMessage("Data saved");
} else {
showWarning("Couldn't save data");
}
});
44. WRAPPING A GENERATOR
function spawn(generatorConstructor) {
var generator = generatorConstructor();
function step(input) {
var result = generator.next(input);
var value = Promise.resolve(result.value);
if (result.done) return value;
else return value.then(step);
}
return step();
}
// (Error handling omitted)
45. PATTERN: CONDITIONAL ON ASYNC RESULT
spawn(function* () {
if ((yield isServerAccessible()) &&
(yield isAuthenticationValid())) {
yield saveData();
showMessage("Data saved");
} else {
showWarning("Couldn't save data");
}
});
49. PATTERN: CONDITIONAL ON ASYNC RESULT
async function save() {
if ((await isServerAccessible()) &&
(await isAuthenticationValid())) {
await saveData();
showMessage("Data saved");
} else {
showWarning("Couldn't save data");
}
}
50. PATTERN: COMBINING SEQUENTIAL RESULTS
async function showUserAndTimeline() {
var user = await getUser();
var timeline = await getTimeline(user);
showUser(user);
showTimeline(timeline);
}
51. PATTERN: ASYNC IN A LOOP
async function runTasks(tasks) {
var results = [];
for (task of tasks) {
results.push(await task.run());
}
return results;
}
54. ASYNC/AWAIT ERROR HANDLING
async function mustachify(userId) {
var user = await loadUserFromDatabase(userId);
var friend = await getBestFriend(user);
var photo = await friend.getBestPhoto();
var betterPhoto = await addMustache(photo);
return showPhotoToUser(user, betterPhoto);
}
mustachify(userId).catch(function (error) {
// You can still use catch() - it's promises under the hood
});
55. ASYNC/AWAIT ERROR HANDLING
async function mustachify(userId) {
try {
var user = await loadUserFromDatabase(userId);
var friend = await getBestFriend(user);
var photo = await friend.getBestPhoto();
var betterPhoto = await addMustache(photo);
return showPhotoToUser(user, betterPhoto);
} catch (error) {
// Try/catch now works with promises & async too
}
}
mustachify(userId);
56. GOTCHA: CAN'T AWAIT IN NESTED FUNCTIONS
async function getAllFriendNames() {
var friendData = friendIds.map(function (friendId) {
// This code isn't in an async function,
// so this is a syntax error.
return (await getUserData(friendId));
});
return friendData.map(function (friend) {
return friend.name;
});
}
57. GOTCHA: CAN'T AWAIT IN NESTED FUNCTIONS
async function getAllFriendNames() {
var friendData = friendIds.map(async function (friendId) {
return (await getUserData(friendId));
});
// Map() doesn't care about async, so our getUserData
// calls haven't actually finished yet.
return friendData.map(function (friend) {
return friend.name;
});
}