Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Promises Are So Passé

Tim Perry
September 02, 2016

Promises Are So Passé

Tim Perry

September 02, 2016
Tweet

More Decks by Tim Perry

Other Decks in Programming

Transcript

  1. "Any fool can write code that a computer can understand.

    Good programmers write code that humans can understand." — Martin Fowler
  2. 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
  3. 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); }); }); }); }); }
  4. PATTERN: CROSS-OPERATION ERROR HANDLING function mustachify(userId, callback) { try {

    loadUserFromDatabase(userId, function (err, user) { if (err) callback(err); else { try { user.getBestFriend(function (err, friend) { if (err) callback(err); else { try { friend.getBestPhoto(function (err, photo) { if (err) callback(err); else { try { addMustache(photo, function (err, betterPhoto) { if (err) callback(err); else { try { showPhotoToUser(user, betterPhoto, callback); } catch (e) { callback(e) } } }); } catch (e) { callback(e) } } }); } catch (e) { callback(e) } } }); } catch (e) { callback(e) } } }); } catch (e) { callback(e) } }
  5. PROMISES RECAP A promise is a representation of a (potentially)

    ongoing process. ▸ Pending ▸ Fulfilled ▸ Rejected
  6. 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"));
  7. PROMISES RECAP Catch errors across steps: loadData().then(function (data) { return

    transformData(data); }).then(function (transformedData) { showData(data); }).catch(function (error) { console.log(error); });
  8. PROMISES RECAP Combine promises: var allData = Promise.all([ loadData("company-one"), loadData("company-two")

    ]); var firstResponse = Promise.race([ getDataFromDataCenter1(), getDataFromDataCenter2() ]);
  9. PATTERN: COMBINING SEQUENTIAL RESULTS - Get the current user's data

    - Load the timeline for that user - Show the user's details & timeline on screen
  10. PATTERN: COMBINING SEQUENTIAL RESULTS var user; getUser().then(function (userData) { user

    = userData; // <- Ick return getTimeline(user); }).then(function (timeline) { showUser(user); showTimeline(timeline); });
  11. 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
  12. 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; }
  13. 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
  14. 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"); } });
  15. function* generateEveryNumber() { var n = 0; while(true) { yield

    n; n += 1; } } var allNumbers = generateEveryNumber(); allNumbers.next(); // { value: 0, done: false } allNumbers.next(); // { value: 1, done: false } allNumbers.next(); // { value: 2, done: false }
  16. 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 }
  17. 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 }
  18. 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
  19. PATTERN: CONDITIONAL ON ASYNC RESULT spawn(function* () { if ((yield

    isServerAccessible()) && (yield isAuthenticationValid())) { yield saveData(); showMessage("Data saved"); } else { showWarning("Couldn't save data"); } });
  20. 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)
  21. PATTERN: CONDITIONAL ON ASYNC RESULT spawn(function* () { if ((yield

    isServerAccessible()) && (yield isAuthenticationValid())) { yield saveData(); showMessage("Data saved"); } else { showWarning("Couldn't save data"); } });
  22. PATTERN: CONDITIONAL ON ASYNC RESULT async function save() { if

    ((await isServerAccessible()) && (await isAuthenticationValid())) { await saveData(); showMessage("Data saved"); } else { showWarning("Couldn't save data"); } }
  23. PATTERN: COMBINING SEQUENTIAL RESULTS async function showUserAndTimeline() { var user

    = await getUser(); var timeline = await getTimeline(user); showUser(user); showTimeline(timeline); }
  24. PATTERN: ASYNC IN A LOOP async function runTasks(tasks) { var

    results = []; for (task of tasks) { results.push(await task.run()); } return results; }
  25. 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 });
  26. 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);
  27. 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; }); }
  28. 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; }); }
  29. GOTCHA: CAN'T AWAIT IN NESTED FUNCTIONS async function getAllFriendNames() {

    var friendData = await Promise.all( friendIds.map(function (friendId) { return getUserData(friendId); }) ); return friendData.map(function (friend) { return friend.name; }); }
  30. Available in Chrome Behind a flag in Edge Coming in

    FireFox 52 Implemented in WebKit