Skip to content

Instantly share code, notes, and snippets.

@ktilcu
Last active August 21, 2017 22:48
Show Gist options
  • Save ktilcu/b6e9ce205038572f3646896ba2460256 to your computer and use it in GitHub Desktop.
Save ktilcu/b6e9ce205038572f3646896ba2460256 to your computer and use it in GitHub Desktop.
kyle-Opinion: Async/Await vs Promises

I was reading this article about why A/A is better than Promises and saw a few code examples stood out to me as being unfair. I am not saying A/A is not better, I just think we need to be a lot more explicit about why it's better. Read below and I will go through each section in the article.

Overall, I think the main benefit of A/A is readability for people who are not familiar with Promises. The problem is that in order to understand A/A at a useful depth, you have to understand promises. In my mind, we are just covering one problem with another and making YAA (yet another abstraction) that people have to understand in order to figure out async programming.

I think a post talking about how to do promises well would be much more useful than one that shows that new language features are better than bad code.

This was supposed to show how chaining in promises made everything long and unwieldy and then how A/A makes it better.

With my solution we are at the same line length but now we have a reusable function for the next time we need to do something like that.

// Bad Promises
function promiseChain () {
  const api = new Api()
  let user, friends
  api.getUser()
    .then((returnedUser) => {
      user = returnedUser
      return api.getFriends(user.id)
    })
    .then((returnedFriends) => {
      friends = returnedFriends
      return api.getPhoto(user.id)
    })
    .then((photo) => {
      console.log('promiseChain', { user, friends, photo })
    })
}
// Great Async
async function asyncAwaitIsYourNewBestFriend () {
  const api = new Api()
  const user = await api.getUser()
  const friends = await api.getFriends(user.id)
  const photo = await api.getPhoto(user.id)
  console.log('asyncAwaitIsYourNewBestFriend', { user, friends, photo })
}
// My Take
const fanout = (src, ...consumers) => Promise.all(consumers.map(consumer => src.then(consumer)));

function promiseChain () {
  const api = new Api()
  cont user = api.getUser()
  const [friends, photo] = fanout(user.then(u=>u.id), [api.getFriends, api.getPhoto])
  Promise.all([user, friends photo]).then(console.log)
}

I think this is an especially bad example of promises in use. The A/A version is simpler not because of A/A but because of implementation. The Parallel version in the next section is essentially the same implementation as my take on the promises one.

// Bad Promises
function promiseLoops () {  
  const api = new Api()
  api.getUser()
    .then((user) => {
      return api.getFriends(user.id)
    })
    .then((returnedFriends) => {
      const getFriendsOfFriends = (friends) => {
        if (friends.length > 0) {
          let friend = friends.pop()
          return api.getFriends(friend.id)
            .then((moreFriends) => {
              console.log('promiseLoops', moreFriends)
              return getFriendsOfFriends(friends)
            })
        }
      }
      return getFriendsOfFriends(returnedFriends)
    })
}
// Great A/A!
async function asyncAwaitLoops () {
  const api = new Api()
  const user = await api.getUser()
  const friends = await api.getFriends(user.id)

  for (let friend of friends) {
    let moreFriends = await api.getFriends(friend.id)
    console.log('asyncAwaitLoops', moreFriends)
  }
}
// My Take - get Users friend's friends
function promiseLoops () {  
  const api = new Api()
  const friendsOfFriends = api.getUser()
    .then(u=>u.id)
    .then(api.getFriends)
    .then(friends=>friends.map(u=>api.getFriends(u.id)))
  Promise.all(friendsOfFriends).then(console.log)
}

Again, this seems like a misrepresentation of Promises. There are bad ways to do promises and, yes, good A/A is better than bad Promises.

// Bad Promises
function callbackErrorPromiseChain () {
  const api = new Api()
  let user, friends
  api.getUser()
    .then((returnedUser) => {
      user = returnedUser
      return api.getFriends(user.id)
    })
    .then((returnedFriends) => {
      friends = returnedFriends
      return api.throwError()
    })
    .then(() => {
      console.log('Error was not thrown')
      return api.getPhoto(user.id)
    })
    .then((photo) => {
      console.log('callbackErrorPromiseChain', { user, friends, photo })
    })
    .catch((err) => {
      console.error(err)
    })
}
// Great A/A!
async function aysncAwaitTryCatch () {
  try {
    const api = new Api()
    const user = await api.getUser()
    const friends = await api.getFriends(user.id)

    await api.throwError()
    console.log('Error was not thrown')

    const photo = await api.getPhoto(user.id)
    console.log('async/await', { user, friends, photo })
  } catch (err) {
    console.error(err)
  }
}
// My Take - exactly the same as the first example
const fanout = (src, ...consumers) => Promise.all(consumers.map(consumer => src.then(consumer)));

function callbackErrorPromiseChain () {
  const api = new Api()
  const user = api.getUser() // can catch here
  const [friends, photo] = fanout(user.then(u=>u.id), [api.getFriends, api.getPhoto])
  Promise.all([user, friends photo]).then(console.log).catch(console.error)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment