Async-Await is essentially kind of a syntactic sugar which helps simplify things and reduce the amount of code. We'll go through some examples to understand it better. Please go through them in a chronological order, each explanation depends on your understanding of the one before.

1. Promise Resolution

Consider the following code fragment:

const getEnginePower = (name) => {
	const cars = {
		ford: 1499,
		toyota: 3200,
		chevy: 1999,
	};

	return Promise.resolve(cars[name]);
};

getEnginePower("ford").then(console.log);

When you use the async keyword instead, it takes the return value and automatically resolves it as a promise. However, the added benefit is that you can get a context to use the await keyword now.

const getEnginePower = async (name) => {
	const cars = {
		ford: 1499,
		toyota: 3200,
		chevy: 1999,
	};

	return cars[name];
};

getEnginePower("ford").then(console.log);

Both results in the following output:

1499

2. Reducing Complexity

Consider the getEnginePower function defined previously. We'll call that from another function, first using promises.

const getCars = () => {
	let a;
	return getEnginePower("toyota")
		.then((v) => {
			a = v;
			return getEnginePower("chevy");
		})
		.then((v) => [a, v]);
};

getCars().then(console.log);

Now if we use async-await, we can simplify this down to:

const getCars = async () => {
	const a = await getEnginePower("toyota");
	const b = await getEnginePower("chevy");

	return [a, b];
};

Both results in the following output:

[3200, 1999]

3. Fatal Flaw

The function getCars simplified with async-await has a fatal flaw. In order to understand that, let's modify the getEnginePower function and add a thread blocking loop in it.