loading...

Clarifying Async and Promises

warrend profile image Daniel Warren ・2 min read

I wanted to clearly present async and promises since the concepts were hard for me to understand at first. Here’s my attempt:

Synchronous javascript means to wait for one action to complete before moving onto the next. For example, imagine a line of five small tables. On all of the tables is a coin, with the exception of the second table, which contains a spinning top. Your job is to go along to each table, flip the coins and spin the top. If you’re doing this synchronously, you would go to the first table, flip the coin and move to the next table. But at the second table, you spin the top but you can’t move to the next table until the top stops spinning. In the real world, this can lead to a bad user experience if you have to wait for something to finish before the rest of the page loads.

Asynchronous requests solve this problem. Instead of waiting for each function to finish, the action is passed to a thread outside of JS. When the request is fulfilled, it’s added to the end of the thread and the callback function will fire. So in our table example, once you spin the top, you can move onto the next table and flip the coin. When the top stops spinning, the table will get shifted to the end of the queue and be ready once you reach the end of the tables.

This ties into promises. A promise is essentially an object that represents an action that hasn’t finished yet but will finish at some point down the road. This makes it easier to handle multiple async requests. When the promise has resolved, we can use the .then function to do something with the returned data. This also makes it easier to chain .then requests together. For example, if we make a request to a server for a token, we want to wait for the token to come back. Once we receive the token, we parse it, and finally do something with that data.

Another example would be if we had an application that is making three separate API requests. Say one API request is for an image, another for the comments, and another for the user data. But we want to wait for all the data to come back before we update the UI so we can use a promise to wait for all the data to finish and then display it to the user. We can use .all to accomplish this with promises.

As a final example below to illustrate promises, we see that A will fire first. Then a fetch request is fired where we wait for a promise from C and then for D. In the meantime, B is fired while we wait. Once C resolves, it fires, then finally D resolves and it fires.

apiCall = () => {
    console.log(A')
    fetch('http://someAPI/data')
        .then(res => {
            console.log(C)
            return res.json()
    })
        .then(theData => console.log(D, theData))
        console.log(B)
}

Discussion

pic
Editor guide
Collapse
rhymes profile image
rhymes

Thanks Daniel, for the refresh. I still haven't quite internalised JavaScript promises completely so fresh takes are always welcome.

I was also exploring the other day the possibilities beyond the "then/catch" pattern with promises because to me it still looks like callbacks, neater and cleaner but there has to be a better way, for readability's sake.

In Python land (see the Twisted framework which influenced Node.js) this problem has been already met. In Twisted promises are called "deferreds" but the issue is the same: cascading callbacks, error handlers, callbacks to your error handlers, sibling callbacks and error handlers can still become a mess to read and understand:

.then(() => {}).then(() => {}).then(() => {}).catch((err) => {})

or in Twisted

.addCallback(function).addCallback(function).addCallback(function).addErrback(errHandler)

What they came up with is:

a decorator named inlineCallbacks which allows you to work with Deferreds without writing callback functions.

So in Twisted you can do this:

@inlineCallbacks
def getUsers(self):
    try:
        responseBody = yield makeRequest("GET", "/users")
    except ConnectionError:
       log.failure("makeRequest failed due to connection error")
       return []

   return json.loads(responseBody)

makeRequest returns a deferred (a promise) and this way instead of attaching callbacks and error handlers to it you can wait for the response to come back and if an error arises you handle it there and then with try...except (try/catch in JS). In the latest Python versions you can even do this:

async def bar():
    baz = await someOtherDeferredFunction()
    fooResult = await foo()
    return baz + fooResult

So you can basically use await for the deferred/promises to resolve and write synchronous-looking code instead of attaching callbacks, which brings me back to JavaScript and async/await (same keywords of Python, don't know which came first :D).

Instead of attaching callbacks and error handler to your promise you can use async/await to write more readable code:

async function bar() {
  const a = await someFunction();
  const b = await someOtherFunction();
  return a + b;
}

I found this video by Wes Bos very informing:

ps. this should probably be a separate post since it's so long :-D

Collapse
warrend profile image
Daniel Warren Author

Thanks for this. Great stuff!