DEV Community

Discussion on: Explain Async/Await Like I'm Five

Collapse
 
fluffynuts profile image
Davyd McColl • Edited

JavaScript, by design, is not multithreaded. So unlike your operating system, it can't do two things at once.
However, there are times we need to wait for stuff to come back from the server. In this case, JavaScript can ask the browser to do the work and be notified when that work is done

This is asynchronous programming - having something done in the background and getting notified when it's done.

Originally, we did this with callbacks - so we'd say "hey browser, please do this work, and when you're done, here's a function you can call with the results"

The problem with callbacks is that we often have to do a few things in the background (asynchronously), so the code starts to get difficult to understand, with many layers of nested callbacks.

Along came the promise, which meant that our code could flatten out a bit. Callback code like this:

$.get("/api/request1", function(result1) {
  $.get("/api/request2?q=" + result1, function(result2) {
    console.log(result1 + " - " + result2);
  })
);
Enter fullscreen mode Exit fullscreen mode

Could be a little simpler:

$.get("/api/request1").then(function(result1) {
  return $.get("/api/request2?q=" + result1);
}).then(function(result2) {
  console.log(result1 + " - " + result2);
});
Enter fullscreen mode Exit fullscreen mode

(EDIT: as noted below, the code above has a bug; I'm not going to remove it, because that would invalidate comments below, but do check out fixes for this code, as shown below!)

Which was a little easier to read and write (and error handling was neater, with .catch())

async/await is a nice way that modern JavaScript let's us use promises with even less code and nesting:

var result1 = await $.get("/api/request1");
var result2 = await $.get("/api/request2?q=" + result1);
console.log(result1 + " - " + result2);
Enter fullscreen mode Exit fullscreen mode

And you can use try/catch to handle errors.

Under the hood, helpers like Babel and TypeScript translate the code above into a version a lot like the promises version, using an old, clever JavaScript feature called generator functions. Don't worry about it too much now though (:

Hope this helps. Apologies for any typos - I'm doing this from my phone.

Collapse
 
jaminfarr profile image
Ben Farr • Edited

Edit: Sorry I didn't see the example above the flattened one which is the same as one of my versions.

Your promise example has a bug but it's a really good example where async/await shines over promises.

$.get("/api/request1").then(function(result1) {
  return $.get("/api/request2?q=" + result1);
}).then(function(result2) {
  // result1 isn't in scope here and will crash
  console.log(result1 + " - " + result2);
});
Enter fullscreen mode Exit fullscreen mode

result1 isn't accessible in the final then. With promises you lose the simplicity of the .then chain if you want both the first return value and to use it as a trigger for a follow-up request.

These are some ways to do it with promises.

// Store result1 in a higher scope
let result1;

$.get("/api/request1")
  .then(function (_result1) {
    result1 = _result1;
    return $.get("/api/request2?q=" + result1);
  })
  .then(function (result2) {
    console.log(result1 + " - " + result2);
  });

// Bring the scope of the result2 then within the result1 then
$.get("/api/request1").then(function (result1) {
  return $.get("/api/request2?q=" + result1).then(function (result2) {
    console.log(result1 + " - " + result2);
  });
});

// Add result1 to second request's response value
$.get("/api/request1")
  .then(function (result1) {
    return $.get("/api/request2?q=" + result1).then(function (result2) {
      return [result1, result2];
    });
  })
  .then(function (results) {
    console.log(results[0] + " - " + results[1]);
  });

// Use request1 as trigger for request2 and combine the requests/results
const request1 = $.get("/api/request1");
const request2 = request1.then(function (result1) {
  return $.get("/api/request2?q=" + result1);
});

Promise.all([request1, request2]).then(function (results) {
  console.log(results[0] + " - " + results[1]);
});
Enter fullscreen mode Exit fullscreen mode

With async/await, as Davyd has shown above, you have a single scope. No workarounds are needed.

var result1 = await $.get("/api/request1");
var result2 = await $.get("/api/request2?q=" + result1);
console.log(result1 + " - " + result2);
Enter fullscreen mode Exit fullscreen mode
Collapse
 
fluffynuts profile image
Davyd McColl • Edited

Nice catch. I could hardly see all the text on my phone when I was typing. Was bound to have at least one glaring flaw (:

Now, I'm at a pc, I think I'd probably correct to:

$.get("/api/request1").then(function(result1) {
  return $.get("/api/request2?q=" + result1).then(function(result2) {
    return { result1, result2 };
  });
}).then(function(results) {
  console.log(results.result1 + " - " + results.result22);
});

(assuming I can use short notation for objects in this particular js-world)

And I think that illustrates even better why async/await is a win (: