JavaScript Promises: All you need to know
Tags: javascript
Published: 2020-06-30 11:36:21

Without any doubt: The most frequent suggestion we see corrected in the wild is actually a pretty trivial one (who would have thought). We found roughly 20,000 changes in our training repos addressing one thing: Unhandled rejections in promises. Maybe it is time to provide a fundamental guide.

If you want your code to be scanned, just go to [deepcode.ai](https://deepcode.ai).

##Why are promises needed?

JavaScript provides a single threaded environment: No two pieces of code run at the same time. This reduces issues regarding consistency with mutexes (think race conditions), but add the need for others. JavaScript makes use of callback functions to provide asynchronous calculations. While this in itself is a possible way to write asynchronous code, it leads to what is known as _Pyramid of Doom_ where you have callback in callback in callback in ... until you totally lose track what happens when. Here come promises to help.

**A promise is an object that represents eventual completion or failure of an asynchronous operation and its subsequent result value.**

Dive deeper with [MDN docs on Promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise) or [Javascript.info](https://javascript.info/async)

##Basic Functionality

```javascript
let promise = new Promise(function(resolve, reject) {
	// Here is the workload
	
	if( /* Success */) {
		/* result can be a Promise or thenable */
		resolve(result)
	}
	else {
		/* Best practice: reason is instanceof Error */
		reject(reason)
	}
})
``` 
Within a promise, two callback functions are provided: `resolve()` shall be called in the case of a positive outcome. The result can be handed on as a parameter. `reject` shall be called in the case of an error (including an explanation as a parameter). There is no third option (like cancelled or whatever). A promise is always in one of three states:
* _pending:_ Initial state and still work in progress
* _fulfilled:_ Completed successfully
* _rejected:_ Operation failed

##Usage
Promises can be hand-on within the app as long as their value is not directly needed. This gives the system the opportunity to resolve whatever is asked in the background without the need to wait for things to settle. When the application has the need for the result to proceed, it can query the result and react upon it.

_Side note:_ Try to delay the need for a result of a promise as much as possible to gain the maximum benefit of using promises.

Example: 

```javascript
const promise1 = new Promise((resolve, reject) => {
	resolve('Here is the result');
});

promise1.then(/* Success */(value) => {
	// Do something with the result
	},
	/* Fail */ (error) => {
	// React on error case
	}
);
```

If the success handler is not provided (as a function), it is replaced by the identity function (aka, it returns the parameter). 

This is the lifecycle as provided by Mozilla's documentation (yet omitting `finally()`):

![Promises Lifecycle](https://mdn.mozillademos.org/files/15911/promises.png)

As you can see in the life cycle: There are two options to provide an **error handler: Either by providing a callback as a parameter for then or by explicitly providing a callback in catch.**
 

##async and await

`async` in front of a function means the function always returns a promise. If the function returns a different type, it is wrapped in a promise.

```javascript
async function f() {
	return 42; // We will get a promise with result 42 and success state
}

f().then(console.log) //prints 42
```

So far, we only stacked on promises but what if we really need the value to be settled. Here comes `await`. This keyword makes JavaScript wait until the promise is settled. Obviously, while the system stops here and waits with the execution of the code until the promise is settled, it continues to execute other code. We will see more sophisticated methods to combine promises a little later.

```javascript

async function f() {
	let promise = new Promise((resolve, reject) => {
		// Do something here
		resolve(result);
	});
	
	let result = await promise;
}

f();

```


**Note:** `await` only works inside an `async` function. You cannot use `await` on top level code but you can wrap it in an anonymous async function:

```javascript
(async () => {
	let response = await promise1;
})();
```



##Chaining of Promises

[Nice in-depth article on the topic here](https://javascript.info/promise-chaining)
The result of a promise can be piped through subsequent functions. Those functions can provide alternative parameters to their subsequent followers allowing to build a pipeline to manipulate data such as filtering or enriching. The then function returns a promise itself.

```javascript
new Promise((resolve, reject) => {
	// Promise
	resolve(result);
}).then((result) => {
	// First handler
	return handler_result;
}).then((handlerResult) => {
	// Second handler
	return secondHandlerResult;
}).then((secondHandlerResult) => ...
```

Objects providing a `then` function, are called _thenable_. These objects can be used with then or await as we saw above.

```javascript
class Thenable {
	then(resolve,reject) {
		if(success) //in case of success 
			resolve(result);
		else
			reject(error);
	}
};

async function f() {
	let result = await new Thenable();
	}
	
f();
```

There are two more important functions:

```javascript
const promise1 = new Promise((resolve, reject) => {
	throw 'Error'; // calling reject() also leads to rejected state
	})
	
promise.catch((error) => {
	// Handle your error here
	}).finally(() => {
	//Do clean up needed whether success or failure
	});
```

* _catch:_ This is actually a shortcut for `then(undefined, onRejected)` and provides a promise that handles error cases. It does an implicit try-catch (as shown above).
* _finally:_ Again a promise which is called in both end states. Helps to reduce code doubling in `then` and `catch` as you can put all clean up needed in both cases in here. 

_Side note:_ Since both `catch()` and `finally()` return promises, you can chain one them again by using `then`.

If there is no error handler provided (_"unhandled rejection"_), the error bubbles up and leads to the script crashing with an error message. This is what DeepCode complains when it finds an unhandled rejection. 


##Combination of Promises

Promises can be collected and combined in various ways. In general, combining promises in a iterable object and provide this to the combination.

```javascript
Promise.all([promise1, promise2, promise3, promise4]).then((values) => {
	console.log(values); // Result Array [result1, result2, result3, result4]
	}).catch((error) => {
	// something went wrong
	})
```

Here is a table of all combination functions:

|Function|Explanation|Typical Use |Reaction on Rejection
|---|---|---|---|
|Promise.all()|Returns a single promise to collect all results of the input promises|Multiple asynchronous tasks that are dependent on each other|Will reject immediately on one input promise reject| 
|Promise.allSettled()|Returns a single promise to collect all results of the input promises|Multiple asynchronous tasks that are not dependent on each other|Will collect all results and rejects|
|Promise.race()|Returns a single Promise that returns as soon as one of the input promises resolve or rejects|Used in batching or for time-outs|Immediately returns on one input rejects|
|Promise.any()(still experimental)|Returns a single Promise which resolves as soon as one of the input promises resolves but wait for all promises to reject before reject|When two resources race to provide data (e.g., cache versus network)|Swallow rejections until all input promises reject|


##Next Steps
From our point of view, knowing the above should equip you to understand and use promises like a pro. Did we miss something, let us know. And, make sure to check your code on [deepcode.ai](https://deepcode.ai).