Let's consider the following code:
function getData() {
$.get('https://jsonplaceholder.typicode.com/posts/1')
.then((data) => {
return data;
});
}
let result = getData();
console.log(result);
We're using a promise and returning a value. The question is then what will be printed to the console. Have a guess?
Turns out, it's undefined
.
Yup. Undefined.
That right there is one of the most confusing aspects of using promises. We're used to using return
and having it return a value out of the function we've created. And, that's exactly what happened. The confusion is what function it actually returned from.
In our example, the value is returned from the delegate we've created. That value is not bubbled back up to the containing function (getData
in our case). This makes creating helper functions like this a challenge at best.
And let's be honest. While promises are certainly cleaner than callbacks, there's still room for improvement. There's still a bit of nesting that's taking place, and it'd be nice if we could avoid that even still.
Before we go any further, one thing we absolutely need to highlight is the fact that async/await is still very cutting edge. Many engines do not yet support the syntax. You will also find a good number of JavaScript developers are also unfamiliar with how async/await works, so you'll need to talk with your team and decide on a standard.
OK, now that we got that out of the way, let's talk about how to use async/await.
Async/await builds on promises. It's still the same concept - we call code that will be executing a long running operation, and we're handed back an IOU which guarantees our code will be executed when it completes. The difference is how we let the engine know what we want executed.
Let's start with a fundamental example, and then return to the problem posed at the beginning. Let's make a call, and then print the value.
function getData() {
$.get('https://jsonplaceholder.typicode.com/posts/1')
.then((data) => {
console.log(data);
});
}
getData();
We've seen this style of code before. We're going to stick with the same concept, only how we collect the value will change.
function async getData() {
let data = await $.get('https://jsonplaceholder.typicode.com/posts/1');
console.log(data);
}
getData();
Our updated getData
is logically the same as the one we saw previously. They key is the await
call. What await
tells the runtime "Hey there! This call I'm about to make to get
is going to take a few moments. Please don't shut things down, and put whatever the result of the function is and put it in the variable data
." Pretty neat, huh?
Without the call to await
, get
will return a Promise
, which obviously isn't the data.
You'll also notice the async
flag on the function. This is required, as it lets the runtime know an await
will be coming at some point.
Towards the beginning of this write-up we talked about the issue returning values when using promises. The delegate we pass into then
is a function in and of itself, so when we call return
, that function exits, but the return value isn't bubbled up. This can be rather confusing, and a challenge to deal with. The simplest solution? Async/await!
If you're going to be grabbing the value returned from a function using async/await, you must wrap that in an async function itself. Otherwise, you'll just get the promise back. I know, it's annoying and not the clearest bug.
Here's the original issue:
function getData() {
$.get('https://jsonplaceholder.typicode.com/posts/1')
.then((data) => {
return data;
});
}
let result = getData();
console.log(result);
Let's update that to use async/await
async function getData() {
let data = await $.get('https://jsonplaceholder.typicode.com/posts/1');
return data;
}
async function displayData() {
let result = await getData();
console.log(result);
}
displayData();
If you run the code you'll notice the result is properly displayed.
You will find that many libraries support both callbacks and promises, and might be wondering which you should use. In my experience, most current development is done using promises, and it provides for cleaner code than callbacks.
As for promises versus async/await, that's very much dependent on the team. On my team, we lean heavily towards async/await. But I know many developers who I greatly respect who use promises. If you ask me - go with async/await. But, as I said up front, make sure your runtime supports the syntax (or use TypeScript).
Regardless of what you choose, be consistent, and communicate with your team!
Hope all of this helped! Feel free to reach out to me at @geektrainer if you have more questions!