Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Background #1

Open
ForbesLindesay opened this issue Dec 16, 2012 · 7 comments
Open

Background #1

ForbesLindesay opened this issue Dec 16, 2012 · 7 comments

Comments

@ForbesLindesay
Copy link
Member

Resolvers represent the method for resolving a promise. In essence we need to decide upon the means for promises to be created.

The resolver needs a method for fulfilling and rejecting a promise. It also needs a way to handle cancellation and report progress.

There are two basic options for how we provide the API (from what has been used in the past):

Option 1

Have a defer() method which returns a 'deferred': {resolver, promise}.

Option 2

Have a defer(fn) method which returns a promise and calls fn with the means to resolve that promise.

@domenic
Copy link
Member

domenic commented Dec 16, 2012

One major issue is whether trying to resolve a promise after it's already resolved should throw or fail silently. Most promise libraries choose the latter, since it allows you to hand out resolver instances to multiple mutually-suspicious users, who cannot then observe the state of the promise they are affecting (i.e. you can create "resolve races").

@briancavalier
Copy link
Member

I like the simplicity and separation of promise and resolver that the defer(fn) style API provides, but I think the function signature of fn may become tricky. The simplest form is great: fn(fulfill, reject), but extensibility is a problem. For example, what do we do when we add progress? and then cancellation? and then features x, y, and z?

One suggestion made by @lsmith was: fn(fulfill, reject, object) where object will be the point of expansion for additional features. Another option might be: fn(resolver) where resolver is an object with at least fulfill and reject methods, and other APIs as necessary.

That said, it seems that something like var deferred = defer() is the more commonly used approach in the wild right now.

I feel pretty strongly that calls to fulfill/reject should be not distinguishable from one another based on the state of the promise. Another tricky situation here is that some promise implementations (when.js included) return a promise from their fulfill/reject methods. In when.js's case, if you are the first to fulfill/reject, it returns a promise that is equivalent to the actual promise, and if you are not the first, it returns a promise for whatever you just passed into fulfill/reject. So, ultimately, you can't tell if you were the first or not, or determine the state of the promise.

Given that, we may need to specify somehow that subsequent calls to fulfill/reject "appear outwardly to succeed but don't affect the state or value of the promise".

@domenic
Copy link
Member

domenic commented Dec 17, 2012

To me the only attraction of the defer(fn) style API, as it is referred to here, is that it eliminates the entire "deferred" concept. So that name is horrible. Instead, I'd prefer new Promise(fn).

Given that, I'd be a fan of new Promise(function (resolver) { }).

But even then, personally I think the extra level of indirection and indentation is annoying, and perhaps not worth it. Maybe I'm just used to the deferred concept, but deferred = { resolver, promise } is nice and simple, IMO.

@briancavalier
Copy link
Member

Ah, the indentation point is a good one, imho. It just makes everything messier, requires more function hoisting, etc.

I understand why new Promise might be good looking ahead to a language-level Promise. I'm just not a huge fan of new in JS. Personal perference.

I also very much like the {resolver, promise} pair syntax, which also (eventually) gets rid of "defer", "deferred", and the like. Maybe then something like let { resolver, promise } = Promise.pending() // or new Promise() makes a good forward-looking API?

@juandopazo
Copy link

Am I too wrong to ask why standardize resolvers? I'm guessing there will be implementations that won't even have resolvers like (hopefully) the DOM.

@domenic
Copy link
Member

domenic commented Dec 21, 2012

@juandopazo That's a fair point. It's not nearly the interoperability concern that the base Promises/A+ spec is, or even things like progress and cancellation. (Although, see #5!)

I honestly approach this repo as a bunch of implementers, none of whom are terribly happy with their libraries' promise-creation mechanism, all getting together to brainstorm what would be a better approach. That is, nobody's terribly happy with deferreds, so this is an opportunity to collaboratively bounce alternatives around, and hopefully come up with something brilliant that everybody wants to adopt.

@juandopazo
Copy link

I'm not that unhappy with deferreds. I think adding Microsoft's Promise (I'd call it defer) function would help with the boilerplate and that would be enough for me. I think this simplifies it enough:

function somethingAsync() {
  // This doesn't have to be attached to the Promise name
  // You could have Vows.defer, Q.defer, when.defer, Y.defer or whatever
  return Promise.defer(function (fulfill, fail) {
    if (everythingOK) {
      fulfill(someValue);
    } else {
      fail(new Error('ouch!');
    }
  }); // returns a promise
}

I'm glad then that this is a place for discussion and brainstorming since there is a topic I'd like to get your input on: extended promise objects.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants