Open ForbesLindesay opened 11 years ago
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").
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".
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.
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?
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.
@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.
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.
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
andrejecting
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 callsfn
with the means to resolve that promise.