Promises
A JavaScript promise is an object that represents the eventual completion or failure of an asynchronous operation. It is a way to handle asynchronous code in a more structured and predictable way.
Promises are created using the Promise constructor. The constructor takes a function as an argument, which is called the executor function. The executor function takes two arguments: a resolve function and a reject function. The resolve function is called when the asynchronous operation completes successfully, and the reject function is called when the asynchronous operation fails.
Once a promise is created, it can be resolved or rejected using the resolve and reject methods. The resolve method takes a value as an argument, and the reject method takes an error as an argument.
Promises can be chained together using the then method. The then method takes two functions as arguments: a success function and a failure function. The success function is called when the promise is resolved, and the failure function is called when the promise is rejected.
The following code shows how to create a promise and chain it together with another promise:
In this code, the promise1 promise is created and resolved with the value "Success!". The promise2 promise is then chained to the promise1 promise. The promise2 promise will be resolved with the value "Success!" when the promise1 promise is resolved.
Promises are a powerful tool for handling asynchronous code in JavaScript. They make it easier to write code that is more predictable and easier to read.
The Promise object represents the eventual completion (or failure) of an asynchronous operation and its resulting value. A Promise is a proxy for a value not necessarily known when the promise is created. It allows you to associate handlers with an asynchronous action's eventual success value or failure reason. This lets asynchronous methods return values like synchronous methods: instead of immediately returning the final value, the asynchronous method returns a promise to supply the value at some point in the future.
Promise States
A Promise is in one of these states:
- pending: initial state, neither fulfilled nor rejected.
- fulfilled: meaning that the operation was completed successfully.
- rejected: meaning that the operation failed.
The eventual state of a pending promise can either be fulfilled with a value or rejected with a reason (error). When either of these options occur, the associated handlers queued up by a promise's then method are called. If the promise has already been fulfilled or rejected when a corresponding handler is attached, the handler will be called, so there is no race condition between an asynchronous operation completing and its handlers being attached.
A promise is said to be settled if it is either fulfilled or rejected, but not pending.

Essentially, a promise is a returned object to which you attach callbacks, instead of passing callbacks into a function. Imagine a function, createAudioFileAsync(), which asynchronously generates a sound file given a configuration record and two callback functions: one called if the audio file is successfully created, and the other called if an error occurs.
Constructor Syntax
Parameters
executor - A function to be executed by the constructor.
It receives two functions as parameters: resolveFunction and rejectFunction.
The executor is custom code that ties an outcome in a callback to a promise. The executor's signature is expected to be something like:
Any errors thrown in the executor will cause the promise to be rejected, and the return value will be neglected.
Return Value
When called via new, the Promise constructor returns a promise object.
The promise object will become resolved when either of the functions resolveFunc or rejectFunc are invoked.
Note that if you call resolveFunc or rejectFunc and pass another Promise object as an argument,
it can be said to be "resolved", but still not "settled".
If createAudioFileAsync() were rewritten to return a promise, you would attach your callbacks to it instead:
When & How To Use Promises
JavaScript is single threaded, meaning that two bits of script cannot run at the same time; they have to run one after another sequentially. In browsers, JavaScript shares a thread with a load of other stuff that differs from browser to browser. But typically JavaScript is in the same queue as painting, updating styles, and handling user actions (such as highlighting text and interacting with form controls). Activity in one of these things delays the others.
A JavaScript Promise object contains both the producing code and calls to the consuming code:
Clicking on the first button will run the script runPromise1() in which the result of the action where x = 0 causes the promise to call on the success function.
Clicking on the second button will run the script runPromise2() in which the result of the action where x = 5 causes the promise to call on the failure function.
Run With x = 0 Run With x = 5
Response
Sample
Sample With Existing HTML Sample Without Existing HTML
Response
Error Handling
In this code, the promise1 promise is created and rejected with an error. The then method is then used to handle the error. The success function will never be called because the promise was rejected. The failure function will be called and will log the error.
Here is another example of how to handle errors with promises:
In this code, the promise1 promise is created and used to fetch data from a web server.
The then method is then used to handle the success and failure cases.
The success function will be called if the request was successful and will log the data.
The failure function will be called if the request failed and will log the error.
Promises are a powerful tool for handling errors in JavaScript. They make it easier to write code that is more predictable and easier to read.
Multiple Async Calls
A real life example of using Javascript Promise for multiple asynchronous calls
In this example, we are performing three asynchronous operations: fetching the user from the database, fetching the user's profile image, and uploading the profile image to the server. We can chain these operations together using promises, which ensures that they are executed in the correct order.
- First, we call the getUser() function, which returns a promise. This promise will be resolved when the user is fetched from the database. Once the promise is resolved, we can access the user object using the then() method.
- Next, we call the getProfileImage() function, which also returns a promise. This promise will be resolved when the user's profile image is fetched from the server. Once the promise is resolved, we can access the profile image using the then() method.
- Finally, we call the uploadProfileImage() function, which also returns a promise. This promise will be resolved when the profile image is uploaded to the server. Once the promise is resolved, we can send an email to the user confirming the changes using the then() method.
Async & Await
async/await and promises are closely related. async functions return promises, and await is waiting for a promise to be resolved. They are similar, but not the same.
async Function
The async function declaration creates a binding of a new async function to a given name. The await keyword is permitted within the function body, enabling asynchronous, promise-based behavior to be written in a cleaner style and avoiding the need to explicitly configure promise chains.
You can also define async functions using the async function expression.
Syntax
Parameters
name | The function's name. |
---|---|
param (Optional) | The name of a formal parameter for the function. For the parameters' syntax, see the Functions reference. |
statements (Optional) | The statements comprising the body of the function. The await mechanism may be used. |
Description
An async function declaration creates an AsyncFunction object. Each time when an async function is called, it returns a new Promise which will be resolved with the value returned by the async function, or rejected with an exception uncaught within the async function.
Async functions can contain zero or more await expressions. Await expressions make promise-returning functions behave as though they're synchronous by suspending execution until the returned promise is fulfilled or rejected. The resolved value of the promise is treated as the return value of the await expression. Use of async and await enables the use of ordinary try / catch blocks around asynchronous code.
The await keyword is only valid inside async functions within regular JavaScript code.
If you use it outside of an async function's body, you will get a SyntaxError.
await can be used on its own with JavaScript modules.
The purpose of async/await is to simplify the syntax necessary to consume promise-based APIs. The behavior of async/await is similar to combining generators and promises.
Async functions always return a promise. If the return value of an async function is not explicitly a promise, it will be implicitly wrapped in a promise.
For example, consider the following code:
It is similar to:
Note that even though the return value of an async function behaves as if it's wrapped in a Promise.resolve, they are not equivalent. An async function will return a different reference, whereas Promise.resolve returns the same reference if the given value is a promise. It can be a problem when you want to check the equality of a promise and a return value of an async function.
The body of an async function can be thought of as being split by zero or more await expressions. Top-level code, up to and including the first await expression (if there is one), is run synchronously. In this way, an async function without an await expression will run synchronously. If there is an await expression inside the function body, however, the async function will always complete asynchronously.
For example:
It is also equivalent to:
Code after each await expression can be thought of as existing in a .then callback. In this way a promise chain is progressively constructed with each reentrant step through the function. The return value forms the final link in the chain.
In the following example, we successively await two promises. Progress moves through function foo in three stages.
- The first line of the body of function foo is executed synchronously, with the await expression configured with the pending promise. Progress through foo is then suspended and control is yielded back to the function that called foo.
- Some time later, when the first promise has either been fulfilled or rejected, control moves back into foo. The result of the first promise fulfillment (if it was not rejected) is returned from the await expression. Here 1 is assigned to result1. Progress continues, and the second await expression is evaluated. Again, progress through foo is suspended and control is yielded.
- Some time later, when the second promise has either been fulfilled or rejected, control re-enters foo. The result of the second promise resolution is returned from the second await expression. Here 2 is assigned to result2. Control moves to the return expression (if any). The default return value of undefined is returned as the resolution value of the current promise.
Note how the promise chain is not built-up in one go. Instead, the promise chain is constructed in stages as control is successively yielded from and returned to the async function. As a result, we must be mindful of error handling behavior when dealing with concurrent asynchronous operations.
For example, in the following code an unhandled promise rejection error will be thrown, even if a .catch handler has been configured further along the promise chain. This is because p2 will not be "wired into" the promise chain until control returns from p1.
async function declarations behave similar to function declarations — they are hoisted to the top of their scope and can be called anywhere in their scope, and they can be redeclared only in certain contexts.
Examples
Async functions and execution order
await and concurrency
In sequentialStart, execution suspends 2 seconds for the first await, and then another second for the second await. The second timer is not created until the first has already fired, so the code finishes after 3 seconds.
In sequentialWait, both timers are created and then awaited. The timers run concurrently, which means the code finishes in 2 rather than 3 seconds, i.e., the slowest timer. However, the await calls still run in series, which means the second await will wait for the first one to finish. In this case, the result of the fastest timer is processed after the slowest.
If you wish to safely perform other jobs after two or more jobs run concurrently and are complete, you must await a call to Promise.all() or Promise.allSettled() before that job.
Warning: The functions sequentialWait and concurrent1 are not functionally equivalent.
In sequentialWait, if promise fast rejects before promise slow is fulfilled, then an unhandled
promise rejection error will be raised, regardless of whether the caller has configured a catch
clause.
In concurrent1, Promise.all wires up the promise chain in one go, meaning that the operation
will fail-fast regardless of the order of rejection of the promises, and the error will always
occur within the configured promise chain, enabling it to be caught in the normal way.
Rewriting a Promise chain with an async function
An API that returns a Promise will result in a promise chain, and it splits the function into many parts. Consider the following code:
it can be rewritten with a single async function as follows:
Alternatively, you can chain the promise with catch():
In the two rewritten versions, notice there is no await statement after the return keyword, although that would be valid too: The return value of an async function is implicitly wrapped in Promise.resolve - if it's not already a promise itself (as in the examples).
async function expression
The async function keywords can be used to define an async function inside an expression.
You can also define async functions using the async function declaration or the arrow syntax.
Syntax
An expression statement cannot begin with the keywords async function to avoid ambiguity with an async function declaration. The async function keywords only begin an expression when they appear in a context that cannot accept statements.
Parameters
name (Optional) | The function name. Can be omitted, in which case the function is anonymous. The name is only local to the function body. |
---|---|
paramN (Optional) | The name of a formal parameter for the function. For the parameters' syntax, see the Functions reference. |
statements (Optional) | The statements which comprise the body of the function. |
Description
An async function expression is very similar to, and has almost the same syntax as, an async function declaration. The main difference between an async function expression and an async function declaration is the function name, which can be omitted in async function expressions to create anonymous functions. An async function expression can be used as an IIFE (Immediately Invoked Function Expression) which runs as soon as it is defined, allowing you to mimic top-level await. See also the chapter about functions for more information.
Examples
Using async function expression
Async IIFE
An async IIFE allows you to use await and for...await in contexts where top-level await is not available. Here we use an arrow function to define the IIFE, but async function expressions can also be used.
await
The await operator is used to wait for a Promise and get its fulfillment value. It can only be used inside an async function or at the top level of a module.
Parameters
expression | A Promise, a thenable object, or any value to wait for. |
---|
Return value
The fulfillment value of the promise or thenable object, or, if the expression is not thenable, the expression's own value.
Exceptions
Throws the rejection reason if the promise or thenable object is rejected.
Description
await is usually used to unwrap promises by passing a Promise as the expression. Using await pauses the execution of its surrounding async function until the promise is settled (that is, fulfilled or rejected). When execution resumes, the value of the await expression becomes that of the fulfilled promise.
If the promise is rejected, the await expression throws the rejected value. The function containing the await expression will appear in the stack trace of the error. Otherwise, if the rejected promise is not awaited or is immediately returned, the caller function will not appear in the stack trace.
The expression is resolved in the same way as Promise.resolve(): it's always converted to a native Promise and then awaited. If the expression is a:
- Native Promise (which means expression belongs to Promise or a subclass, and expression.constructor === Promise): The promise is directly used and awaited natively, without calling then().
- Thenable object (including non-native promises, polyfill, proxy, child class, etc.): A new promise is constructed with the native Promise() constructor by calling the object's then() method and passing in a handler that calls the resolve callback.
- Non-thenable value: An already-fulfilled Promise is constructed and used.
Even when the used promise is already fulfilled, the async function's execution still pauses until the next tick. In the meantime, the caller of the async function resumes execution. See example below.
Because await is only valid inside async functions and modules, which themselves are asynchronous and return promises, the await expression never blocks the main thread and only defers execution of code that actually depends on the result, i.e., anything after the await expression.
Examples
Awaiting a promise to be fulfilled
If a Promise is passed to an await expression, it waits for the Promise to be fulfilled and returns the fulfilled value.
Thenable objects
Thenable objects are resolved just the same as actual Promise objects.
They can also be rejected:
Conversion to promise
If the value is not a Promise, await converts the value to a resolved Promise, and waits for it. The awaited value's identity doesn't change as long as it doesn't have a then property that's callable.
Handling rejected promises
If the Promise is rejected, the rejected value is thrown.
You can handle rejected promises without a try block by chaining a catch() handler before awaiting the promise.
This is built on the assumption that promisedFunction() never synchronously throws an error, but always returns a rejected promise. This is the case for most properly-designed promise-based functions, which usually look like:
However, if promisedFunction() does throw an error synchronously, the error won't be caught by the catch() handler. In this case, the try...catch statement is necessary.
Top level await
You can use the await keyword on its own (outside of an async function) at the top level of a module. This means that modules with child modules that use await will wait for the child modules to execute before they themselves run, all while not blocking other child modules from loading.
Here is an example of a module using the Fetch API and specifying await within the export statement. Any modules that include this will wait for the fetch to resolve before running any code.
Control flow effects of await
When an await is encountered in code (either in an async function or in a module), the awaited expression is executed, while all code that depends on the expression's value is paused. Control exits the function and returns to the caller. When the awaited expression's value is resolved, another microtask that continues the paused code gets scheduled. This happens even if the awaited value is an already-resolved promise or not a promise: execution doesn't return to the current function until all other already-scheduled microtasks are processed. For example, consider the following code:
In this case, the function foo is synchronous in effect, because it doesn't contain any await expression. The three statements happen in the same tick. Therefore, the two function calls execute all statements in sequence. In promise terms, the function corresponds to:
However, as soon as there's one await, the function becomes asynchronous, and execution of following statements is deferred to the next tick.
This corresponds to:
The extra then() handler can be merged with the executor passed to the constructor because it's not waiting on any asynchronous operation. However, its existence splits the code into one additional microtask for each call to foo. These microtasks are scheduled and executed in an intertwined manner, which can both make your code slower and introduce unnecessary race conditions. Therefore, make sure to use await only when necessary (to unwrap promises into their values).
Microtasks are scheduled not only by promise resolution but by other web APIs as well, and they execute with the same priority. This example uses queueMicrotask() to demonstrate how the microtask queue is processed when each await expression is encountered.
In this example, the test() function is always called before the async function resumes, so the microtasks they each schedule are always executed in an intertwined fashion. On the other hand, because both await and queueMicrotask() schedule microtasks, the order of execution is always based on the order of scheduling. This is why the "queueMicrotask() after calling async function" log happens after the async function resumes for the first time.
Improving stack trace
Sometimes, the await is omitted when a promise is directly returned from an async function.
However, consider the case where lastAsyncTask asynchronously throws an error.
Only lastAsyncTask appears in the stack trace, because the promise is rejected after it has already been returned from noAwait — in some sense, the promise is unrelated to noAwait. To improve the stack trace, you can use await to unwrap the promise, so that the exception gets thrown into the current function. The exception will then be immediately wrapped into a new rejected promise, but during error creation, the caller will appear in the stack trace.
Contrary to some popular belief, return await promise is at least as fast as return promise, due to how the spec and engines optimize the resolution of native promises. There's a proposal to make return promise faster and you can also read about V8's optimization on async functions. Therefore, except for stylistic reasons, return await is almost always preferable.
The Pyramid of Doom
A common need is to execute two or more asynchronous operations back to back,
where each subsequent operation starts when the previous operation succeeds,
with the result from the previous step.
In the old days, doing several asynchronous operations in a row would lead to the classic callback pyramid of doom:
Chaining
With promises, we accomplish this by creating a promise chain.
The API design of promises makes this great, because callbacks are attached to the returned promise object,
instead of being passed into a function.
Here's the magic: the then() function returns a new promise, different from the original:
This second promise promise2 represents the completion not just of doSomething(), but also of the successCallback or failureCallback you passed in, which can be other asynchronous functions returning a promise. When that's the case, any callbacks added to promise2 get queued behind the promise returned by either successCallback or failureCallback.
With this pattern, you can create longer chains of processing, where each promise represents the completion of one asynchronous step in the chain. In addition, the arguments to then are optional, and catch(failureCallback) is short for then(null, failureCallback), so if your error handling code is the same for all steps, you can attach it to the end of the chain:
Longer Chains of Processing
Arrow Functions
This can also be expressed using arrow functions
The Floating Promise Problem
Important Note: Always return results, otherwise callbacks won't catch the result of a previous promise (with arrow functions, () => x is short for () => { return x; }). If the previous handler started a promise but did not return it, there's no way to track its settlement anymore, and the promise is said to be "floating".
doSomething() .then((url) => { // I forgot to return this fetch(url); }) .then((result) => { // RESULT IS UNDEFINED, BECAUSE NOTHING IS RETURNED FROM // THE PREVIOUS HANDLER. // THERE'S NO WAY TO KNOW THE RETURN VALUE OF THE fetch() // CALL ANYMORE, OR WHETHER IT SUCCEEDED AT ALL. });
Race Conditions
This may be worse if you have race conditions, if the promise from the last handler is not returned, the next then handler will be called early, and any value it reads may be incomplete.
const listOfIngredients = []; doSomething() .then((url) => { // I FORGOT TO RETURN THIS fetch(url) .then((res) => res.json()) .then((data) => { listOfIngredients.push(data); }); }) .then(() => { console.log(listOfIngredients); // ALWAYS [], BECAUSE THE FETCH REQUEST HASN'T COMPLETED YET. });
Therefore, as a rule of thumb, whenever your operation encounters a promise, return it and defer its handling to the next then handler.
Nesting
In the two examples above, the first one has one promise chain nested in the return value of another then() handler, while the second one uses an entirely flat chain. Simple promise chains are best kept flat without nesting, as nesting can be a result of careless composition. See common mistakes.
Nesting is a control structure to limit the scope of catch statements. Specifically, a nested catch only catches failures in its scope and below, not errors higher up in the chain outside the nested scope. When used correctly, this gives greater precision in error recovery:
Note that the optional steps here are nested, with the nesting caused not by the indentation, but by the placement of the outer ( and ) parentheses around the steps.
The inner error-silencing catch handler only catches failures from doSomethingOptional()
and doSomethingExtraNice(), after which the code resumes with moreCriticalStuff().
Importantly, if doSomethingCritical() fails, its error is caught by the final (outer) catch only,
and does not get swallowed by the inner catch handler.
Chaining After A Catch
It's possible to chain after a failure, i.e. a catch, which is useful to accomplish new actions even after an action failed in the chain. Read the following example:
This will output the following text:
Chained Promise
Response
Code Trace
- Line 4: A new promise is created with resolve as the success callback and reject as the fail callback.
- Line 2:
- Line 2:
- Line 2:
- Line 2:
Working Example 1
Run Example 1
Response 1
Working Example 2
Run Example 2
Response 2
Working Example 3
Run Example 3
Response 3
Working Example 4
In this example the Promise method all() is called. This processes all items in the given array.
There is another method called race() which will only process the item which finishes first.