Deferred (promise) pattern
Tags: javascript, promise
Published: 2023-04-08 18:25:12

**Deferred** pattern is one of the most commonly used design patterns in JavaScript world and in programming in general. It postpones the action to a later stage for whatever reason, but mostly because the expected value is not available yet. 

[Promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise) falls nicely to this category. It boxes the future value and provides a way to unbox it whether it realizes (then) or fails (catch). The basic usage reveals how the Promise pattern does work:
```javascript
const promise = new Promise(function(resolve, reject) {
	asyncCall(function(err, result) {
		if (err) {
			reject(err);
		} else {
			resolve(result);
		}
	})
})

promise.then(successHandler).catch(rejectHandler)
```
New Promise object is constructed and the resolver function is called some time in the constructor. From inside resolver function the asynchronous call is triggered and once the result comes back the status of the Promise changes from *pending* to *settled* by calling `reject` or `resolved`. At that point any callback provided by calling `.than()` pipe is executed in the same order how they were registered.

From the above is evident that the programmer or user has limited control when the promise settlement start to happen. Calling the constructor makes the things moving right away. Of course unless we inject some handlers from calling scope or we close over the provided `resolve` and `reject` levers.

```javascript
const makeDeferred = () => {
	const deferred = {};
	deferred.promise = new Promise((resolve, reject) => {
		deferred.resolve = resolve;
		deferred.reject = reject;
	});
	return deferred;
}
```
The new object contains `promise` and the handlers to `reject` or `resolve` it.
```javascript
{ promise, resolve, reject }
```
And that's it. In the simple form and shape.


## Deferred as subclass of Promise
The disadvantage if this approach is *hacky* implementation. The Promise object needs to be *thenable* and as per specification it needs to return the Promise object instantiated from the same class as the calling object. This means internally the constructor is called with standard resolver method which we need to support. Hence the check for the resolver argument. Also it is going against type definition where `Promise` expects resolver function in the constructor. Secondly we are robbing ourselves from any potential native performance optimization regarding `Promise` class or it's instance.
```javascript
class DeferredPromise extends Promise {
	constructor(resolver) {
		const that = {};
		super(function(resolve, reject) {
			Object.assign(that, {resolve, reject})
		});
		Object.assign(this, that)
		if (resolver) {
			resolver(this.resolve, this.reject)
		}
	}
}

var deferred = new DeferredPromise();

deferred.then(doStuffWithValue)

deferred.resolve('Hello World')
```
Although this is not recommended, it might be sometimes useful if you have existing middle-ware pipeline working with promise objects and you can get away with dodgy typing.


## Deferred as result of a factory function
Building on the original principle, much cleaner and recommended approach is to user factory method 
```javascript
export function Defer(workload) {
	let _reject, _resolve;

	const _p = new Promise(function (resolve, reject) {
		_resolve = resolve;
		_reject = reject;
	})

	const trigger = (function (workload) {
		let _result = undefined;

		return function() {
			if (_result) {
				return _result;
			}
			return _result = workload()
				.then(data => {
					_resolve(data)
				})
				.catch(reason => {
					_reject(reason)
				})
		}
	})(workload)

	return {
		get promise() { return _p },
        trigger,
	}
}


var deferred = Defer(() => http.get(url))

// in some class or API expecting Promise value
deferred.promise.then(doStuffWithValue)

// some later stage in app
deferred.trigger()
```
This implementation takes the async function returning a Promise and returns the new Promise object wrapping the future promise object returned from the async function and the handler to allow as to trigger the async function in the future.

We can see that the `_reject` and `_resolve` variables are closed over and the `resolve` resp `reject` handlers from the Promise implementation are assigned.
`trigger` method implements simple caching in case it gets called multiple times.

## Wrap up
If anybody find this useful I wrapped all of the above in the platform agnostic `npm package`, which can be installed by
```sh
$ npm install deferable
```
Or it can be found on [github/webduvet/deferable.git](https://github.com/webduvet/deferable.git). It offers both the Class flavor as well as the Factory flavor.

Deferred Promise pattern has many use cases e.g. api call throttling, user input de-bounce, user action timeout etc.

In this the next article I elaborate one particular use case of Deferred Promise pattern - throttling the async calls.