The ES6 introduced Promises as a placeholder for a value that can either resolve or reject at some time in the future.
Promises can help in dealing with tasks that rely on other tasks.
Promises can resolve or reject depending on the condition and status of the function.
Event Loop has two queues: the (macro) task queue and the microtask queue.
Within the Event Loop, Promise gets added to the microtask queue.
Async/Await makes working with Promises easier through the use of await keywords, which suspends the async function as soon as the awaited value gets resolved.
Async functions implicitly return an object that can be used instead of explicitly creating promises using the Promise object.
Async functions are different compared to a promise then because the await keyword suspends the async function.
Working with asynchronous JavaScript may seem overwhelming, but it takes experience to feel confident while working with it.
If you want to know more about promises states and fates, you can check the domenic/promises-unwrapping GitHub repo.