Promises for Promises That Are Yet to Be Created Without Using the Deferred [Anti]Pattern

Promises for promises that are yet to be created without using the deferred [anti]pattern

Promises for promises that are yet to be created

…are easy to build by chaining a then invocation with the callback that creates the promise to a promise represents the availability to create it in the future.

If you are making a promise for a promise, you should never use the deferred pattern. You should use deferreds or the Promise constructor if and only if there is something asynchronous that you want to wait for, and it does not already involve promises. In all other cases, you should compose multiple promises.

When you say

When the API call is queued, the promise for the network request would be created at some point in the future

then you should not create a deferred that you can later resolve with the promise once it is created (or worse, resolve it with the promises results once the promise settles), but rather you should get a promise for the point in the future at which the network reqest will be made. Basically you're going to write

return waitForEndOfQueue().then(makeNetworkRequest);

and of course we're going to need to mutate the queue respectively.

var queue_ready = Promise.resolve(true);
function callAPI(params) {
var result = queue_ready.then(function(API_available) {
return doRealNetRequest(params);
});
queue_ready = result.then(function() {
return true;
});
return result;
}

This has the additional benefit that you will need to explicitly deal with errors in the queue. Here, every call returns a rejected promise once one request failed (you'll probably want to change that) - in your original code, the queue just got stuck (and you probably didn't notice).

The second case is a bit more complicated, as it does involve a setTimeout call. This is an asynchronous primitive that we need to manually build a promise for - but only for the timeout, and nothing else. Again, we're going to get a promise for the timeout, and then simply chain our API call to that to get the promise that we want to return.

function TimeoutQueue(timeout) {
var data = [], timer = 0;
this.promise = new Promise(resolve => {
this.renew = () => {
clearTimeout(timer);
timer = setTimeout(resolve, timeout);
};
}).then(() => {
this.constructor(timeout); // re-initialise
return data;
});
this.add = (datum) => {
data.push(datum);
this.renew();
return this.promise;
};
}

var queue = new TimeoutQueue(10);
function callAPI2(data) {
return queue.add(data).then(callAPI);
}

You can see here a) how the debouncing logic is completely factored out of callAPI2 (which might not have been necessary but makes a nice point) and b) how the promise constructor only concerns itself with the timeout and nothing else. It doesn't even need to "leak" the resolve function like a deferred would, the only thing it makes available to the outside is that renew function which allows extending the timer.

Alternatives to Deferred (anti?-)pattern when awaiting for user interactions in Angular

The problem of the deferred antipattern is not in exposing the resolve function in itself, but in exposing it together with (or worse, as part of) the promise. There's no reason your request class would need to contain the promise. Instead, all you need to do is simply

const response = await new Promise(resolve => {
mediator.send({ respond: resolve });
});

The mediator needs nothing but this object, and the component that handles the request can still simply call request.respond(response). This is much simpler than doing

const request = new Request();
mediator.send(request);
const response = await request.response;

This might be unnecessarily complicated (with all the code in the Request class), but the usage is not problematic yet. Where it really becomes an antipattern is if you did

function sendRequest() {
const request = new Request();
mediator.send(request);
return request;
}

because now someone has a "deferred object", not just a promise for the response. They might abuse the function:

const request = sendRequest();
request.respond("Ooops");
const response = await request.response;

This is the actual danger: returning a deferred to code that is not supposed to resolve the promise. It's totally fine to hand the resolve function to the component that is supposed to respond though.

Can you write this without using a Deferred?

Maybe the below is just a poor man's approach to deferreds, and doesn't really get to the crux of the matter, but instead of a queue of deferreds, you could just keep a queue of resolver functions.

This saves a small amount of code over your approach and avoids explicitly using Deferreds.

I don't know if there is an established pattern for this, but this in itself seems like a reusable pattern for maintaining an asynchronous pool of objects, so rather than calling it WorkerList, you could name it AsyncPool, and then compose that as a reusable piece within your WorkerList:

class AsyncPool {    constructor() {        this.entries = [];        this.resolverQueue = [];    }    add(entry) {        console.log(`adding ${entry}`);        this.entries.push(entry);
// if someone is waiting for an entry, // pull the oldest one out of the list and // give it to the oldest resolver that is waiting while (this.resolverQueue.length && this.entries .length) { let r = this.resolverQueue.shift(); r(this.entries.shift()); } } // if there's an entry, get one immediately // if not, return a promise that resolves with an entry // when next one is available get() { return new Promise((r) => this.entries.length ? r(this.entries.shift()) : this.resolverQueue.push(r) ); }}

let pool = new AsyncPool();
pool.add('Doc');pool.add('Grumpy');pool.get().then(console.log);pool.get().then(console.log);pool.get().then(console.log);pool.get().then(console.log);
// add more entries latersetTimeout(() => pool.add('Sneezy'), 1000);setTimeout(() => pool.add('Sleepy'), 2000);

Is this a Deferred Antipattern?

Is this a “Deferred Antipattern”?

Yes, it is. 'Deferred anti-pattern' happens when a new redundant deferred object is created to be resolved from inside a promise chain. In your case you are using $q to return a promise for something that implicitly returns a promise. You already have a Promise object($http service itself returns a promise), so you just need to return it!

Here's the super simple example of what a service, with a deferred promise and one with antipattern look like,

This is anti-pattern

app.factory("SomeFactory",['$http','$q']){
return {
getData: function(){
var deferred = $q.defer();
$http.get(destinationFactory.url)
.then(function (response) {
deferred.resolve(response.data);
})
.catch(function (error) {
deferred.reject(error);
});
return deferred.promise;
}
}
}])

This is what you should do

app.factory("SomeFactory",['$http']){
return {
getData: function(){
//$http itself returns a promise
return $http.get(destinationFactory.url);
}
}

while both of them are consumed in the same way.

this.var = SomeFactory.getData()
.then(function(response) {
//some variable = response;
},function(response) {
//Do error handling here
});

There's nothing wrong with either examples(atleast syntactically)..but first one is redundant..and not needed!

Hope it helps :)

What is the explicit promise construction antipattern and how do I avoid it?

The deferred antipattern (now explicit-construction anti-pattern) coined by Esailija is a common anti-pattern people who are new to promises make, I've made it myself when I first used promises. The problem with the above code is that is fails to utilize the fact that promises chain.

Promises can chain with .then and you can return promises directly. Your code in getStuffDone can be rewritten as:

function getStuffDone(param){
return myPromiseFn(param+1); // much nicer, right?
}

Promises are all about making asynchronous code more readable and behave like synchronous code without hiding that fact. Promises represent an abstraction over a value of one time operation, they abstract the notion of a statement or expression in a programming language.

You should only use deferred objects when you are converting an API to promises and can't do it automatically, or when you're writing aggregation functions that are easier expressed this way.

Quoting Esailija:

This is the most common anti-pattern. It is easy to fall into this when you don't really understand promises and think of them as glorified event emitters or callback utility. Let's recap: promises are about making asynchronous code retain most of the lost properties of synchronous code such as flat indentation and one exception channel.

Why does the Promise constructor need an executor?

This is called the revealing constructor pattern coined by Domenic.

Basically, the idea is to give you access to parts of an object while that object is not fully constructed yet. Quoting Domenic:

I call this the revealing constructor pattern because the Promise constructor is revealing its internal capabilities, but only to the code that constructs the promise in question. The ability to resolve or reject the promise is only revealed to the constructing code, and is crucially not revealed to anyone using the promise. So if we hand off p to another consumer, say

The past

Initially, promises worked with deferred objects, this is true in the Twisted promises JavaScript promises originated in. This is still true (but often deprecated) in older implementations like Angular's $q, Q, jQuery and old versions of bluebird.

The API went something like:

var d = Deferred();
d.resolve();
d.reject();
d.promise; // the actual promise

It worked, but it had a problem. Deferreds and the promise constructor are typically used for converting non-promise APIs to promises. There is a "famous" problem in JavaScript called Zalgo - basically, it means that an API must be synchronous or asynchronous but never both at once.

The thing is - with deferreds it's possible to do something like:

function request(param) {
var d = Deferred();
var options = JSON.parse(param);
d.ajax(function(err, value) {
if(err) d.reject(err);
else d.resolve(value);
});
}

There is a hidden subtle bug here - if param is not a valid JSON this function throws synchronously, meaning that I have to wrap every promise returning function in both a } catch (e) { and a .catch(e => to catch all errors.

The promise constructor catches such exceptions and converts them to rejections which means you never have to worry about synchronous exceptions vs asynchronous ones with promises. (It guards you on the other side by always executing then callbacks "in the next tick").

In addition, it also required an extra type every developer has to learn about where the promise constructor does not which is pretty nice.

When would someone need to create a deferred?

Does there exist a situation where it would be necessary (or just
better somehow) to use a deferred?

There is no such situation where a deferred is necessary. "Better" is a matter of opinion so I won't address that here.

There's a reason that the ES6 promise specification does not have a deferred object. You simply don't need one. Anything that people used to use a deferred object for can always be done another way that doesn't use a deferred object.

First off, the majority of uses fit very nicely into the promise constructor model. Secondly, any other cases that didn't fit quite so cleanly into that model can still be accomplished with the promise constructor or by starting with a resolved promise and chaining to it.

The main use case I've seen for a deferred is when you want to pass the ability to resolve() or reject() off to some other piece of code other than the code that created the promise. A Deferred made that very easy since you could just pass the deferred object and it had public methods for resolving or rejecting it. But, with a promise, you can also just pass the resolve and/or reject methods. Since they are bound to the specific object automatically, you can just pass the function references. And, in other cases, you can just let the other code create their own promise and resolve/reject it themselves and have that operation linked to yours rather than letting them resolve/reject your promise. Is all that quite as clean as passing a deferred object? Mostly a matter of opinion, but neither are very common use cases and all are something that can be accomplished without a separate deferred object.

And, as torazaburo points out, letting some external code resolve or reject your promise is a bit of an anti-pattern in its own right. You created the promise - you resolve/reject it. If you want to use external events to decide when to resolve/reject it, then have them notify you (via their own promise or callback) and you can resolve/reject your own promise. Or, have them create their own promise that you can use. That's really the desired model. Or, let them chain onto your promise so that the end result is gated by when their operation is done.

If one was used to coding with the deferred object (say with a jQuery deferred), it might take a little getting used to coding without it, but after a little while you just start to think differently and it starts to come natural to just use the promise constructor.

Once you promisify the sphere of async operations that you use in any given application, it's pretty rare that you ever even need to create your own promises any more because you are mostly just building off promises that the async functions you call are already creating or using flow control operations like Promise.all() which create uber promises for you.

This is the main point of anti-patterns. Use the promises that are already created for you rather than manually create more. Chain them. Return promises from .then() handlers to link async operations under logic control.

Of course, there are existing async operations that don't return promises for which somebody needs to create a promise, but that should be done in a promisify layer somewhere outside the purvey of your main coding logic and done only once for that operation and then code that calls the async operation should be able to just use the returned promises.

ES6 Promises - Without using a deferred, how can I guarantee eventual access to an object instantiated during a React componentDidMount function?

I was further surprised to discover a large number of people on blogs referring to the usage of deferred as an anti-pattern.

You seem to refer to the deferred antipattern here, which is about a specific usage of deferreds - it doesn't say that deferreds are an antipattern.

Although deferreds are deprecated nonetheless :-) You don't need any deferred object, the resolve function is absolutely enough.

I can't instantiate the promise outside of the componentDidMount class because it will fire immediately.

Why do you fire it immediately? Just don't do that. You could even wrap it around the class:

export const promise = new Promise(resolve => {
const Foo = React.createClass({
componentDidMount() {
const map = new ol.Map(…);
resolve(map);
}

}
… // do something with Foo here
});

but more likely you will want to do

let resolve;
export const promise = new Promise(r => {
resolve = r;
});
export const Foo = React.createClass({
componentDidMount() {
const map = new ol.Map(…);
resolve(map);
}

}

Of course notice that this is a horrible pattern - you've just created a global, static promise, a singleton basically. It will be resolved when any of the Foo components you might have in your app will get mounted. Don't do that. I would recommend to put the promise in your component's constructor, although I'm still not sure what exactly you need it for.

Using Bluebird Promises, how to solve this with the deferred anti-pattern?

In 3 years of programming with promises, I've only found one situation where using a deferred made my code simpler. I've come to the conclusion that it's a pretty rare situation. By learning the right techniques (using chaining here) one can nearly always avoid them and end up with simpler code that is at less risk of fairly common mistakes (such as incomplete error propagation or uncaught exceptions).

In this particular case, you can chain your subsequent attempts onto the previous promise by returning a new promise from inside a .then() handler. This allows you to return a promise from your connect function, but keep that promise going for future attempts (holding off its eventual resolution) until some future retry attempts succeeds or until you run out of retry attempts.

You can do that like this. Pay particular attempt to what happens inside the connect() function.

function tryCreateWebsocket(){

var attempt = 1;
var ports = [8080, 8080, 8080, 8081, 8082];
var maxAttempts = ports.length;

function connect() {
var curPort = ports[attempt - 1];
return createWebsocket(curPort).catch(function(err){ // Error, retry
if(attempt < maxAttempts){
console.log("- attempt " + attempt + " failed. Retry");
++attempt;

// chain next attempt onto previous promise
return connect();
} else {
// reject here with no more retries
throw new Error("max retry attempts exceeded without successful connection");
}
});
}

// start trying to connect, return a promise
// errors will be caught and subsequent retries will be chained
// onto this first promise until it either succeeds or runs out
// of retry attempts
return connect();
}

// Main Program
// =================================================================
tryCreateWebsocket().then(function(wsServer){
console.log("Websocket succesfully initialized.");
// server instance is valid here, use it for further code
},function(){
console.log("Websocket startup has failed!");
});
// =================================================================

// Helper function: Creating a websocket, with a port as parameter
function createWebsocket(port){
return new Promise(function(resolve, reject){

wsServer = new WebSocket.Server({
perMessageDeflate: false,
port: port
});

wsServer.on("error", reject);
wsServer.on("listening", function() {
resolve(wsServer);
});
});
}

Note, I changed the design to make the wsServer instance the resolved value of the returned promise. Then, you aren't relying on side effects to set that higher scoped variable. You can just get it from the resolved promise and store it where you want at the time you know it's valid.



Related Topics



Leave a reply



Submit