Parameter to promise constructor

# Domenic Denicola (11 years ago)

Anne, Mark, and I are working through the details on revising DOM promises to match the updated AP2 consensus we've developed over these many es-discuss promise threads. There is one change we were hoping to make from the DOM promises spec that has not been discussed before, so we wanted to run it by the list to make sure there were no objections.

All existing promise implementations that use the constructor pattern, e.g. Q, WinJS, YUI, RSVP, when, then/promise, and others, give the constructor arguments as new Promise((resolve, reject) => ...). This was codified in a Promises/A+ strawman which has met with wide agreement among the implementer community.

The current DOM promises draft gives the constructor arguments as new Promise(resolver => ...) where resolver is an instance of the PromiseResolver class and has resolve, reject, and fulfill methods. With the AP2 changes, fulfill no longer is necessary (you will use Promise.of for that use case), so this would be reduced to resolve and reject methods. Note that PromiseResolver is a full-fledged constructor with its own prototype, and the resolve and reject methods are actually methods, i.e. can't be extracted without binding, so in particular using destructuring syntax like new Promise(({ resolve, reject }) => ...) will not work.

Since there's no real advantage to the PromiseResolver approach, and there are a number of disadvantages, we were hoping to switch to the prevalent (resolve, reject) signature in the revised DOM promises spec.

Let us know what you think!

# Nathan Wall (11 years ago)

Domenica Denicola wrote:

Since there's no real advantage to the PromiseResolver approach, and there are a number of disadvantages, we were hoping to switch to the prevalent (resolve, reject) signature in the revised DOM promises spec.

Let us know what you think!

One advantage to the PromiseResolver is that it's easier to pass around than two separate functions.  Passing the resolver around isn't common, but at my workplace we've made use of it in a "requester" pattern.

function Requester() {         this.requests = Object.create();     }          Requester.prototype = {         respond: function(requestName, callback) {             this.requests[requestName] = callback;         },         request: function(requestName, ...values) {             return new Promise(resolver => {                 this.requests[requestName](resolver, ...values);             });         }     };

This allows a general data retrieval mechanism to easily be dropped in place.  For instance, if you are writing a Grid class which should display tabular data and it needs to request new data any time the page is changed or columns or sorted, you can make it a requester.  Then whoever creates a Grid can define how it gets its data:

var grid = new Grid();     grid.respond('load-data', function(resolver, page, sorts) {         var request_params = {             // ...             page: page,             sorts: sorts,             // ...         };         makeAjaxCall(request_params).then(data => {             resolver.resolve(data);         });     });

Whenever the grid wants to load data, it will internally make a "load-data" request.

All of these things could be written to use resolve and reject functions rather than a PromiseResolver object, but having one thing to pass around has been nice in my experience (we call that thing the "Obligation" because if you get it it's your obligation to fulfill the promise).

# Nathan Wall (11 years ago)

Note that this turns the common use case on its head.  The creator of the promise passes the resolver to the outside party and holds onto the promise itself.  That's why it's been helpful two full objects with different roles.

# Tab Atkins Jr. (11 years ago)

On Fri, Aug 30, 2013 at 7:33 AM, Nathan Wall <nathan.wall at live.com> wrote:

One advantage to the PromiseResolver is that it's easier to pass around than two separate functions. Passing the resolver around isn't common, but at my workplace we've made use of it in a "requester" pattern.

function Requester() {
    this.requests = Object.create();
}

Requester.prototype = {
    respond: function(requestName, callback) {
        this.requests[requestName] = callback;
    },
    request: function(requestName, ...values) {
        return new Promise(resolver => {
            this.requests[requestName](resolver, ...values);
        });
    }
};

Just change this to:

this.requests[requestName]({resolve, reject}, ...values);

Changing the argument signature has only a tiny, local effect on your code.

# Mark S. Miller (11 years ago)

Passing around just the resolve function is another way to encode that same functionality with no loss of generality, since one can call the resolve with a rejected promise, in order to reject the previously returned promise. The reject function is just a convenience for that.

(The resolve function by itself is also an attenuation of the resolve function, though this is not relevant to the current example since it would be bundled with the resolve function, negating any attenuation.)