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
promise(function (resolver)) or promise(function (fulfill, reject, progress, cancellationToken, ...)) #7
Comments
Personally I'm fairly happy with option 1 or option 3, but I think option 2 would be a bad idea (frustrating to implement in libraries that don't yet support everything and gets worse and worse if we change/add things). I think I marginally prefer option 1 as I think it remains helpfully consistent with other APIs such as spawn, which might end up looking a bit like this: spawn(function* (resolver) {
var a = yield promiseA;
resolver.reportProgress(0.5);
var b = yield promiseB;
resolver.reportProgress(1);
return a + b;
}); |
I prefer If it needs to be greater than 3, some implementations would add more than 3 regardless of the standard (extending it) or we believe it may need more than 3 at some point in the future, then I prefer |
There are currently these 4 things that we know we'll need to pass to the function based on the current specs in development. I believe Q might have some other wacky way to semi-resolve a promise @kriskowal ? I'm planning to make a library that supports hot/cold promises that start only when you call then. So this number is going to increase, but we could just separate out Another alternative: return new Promise(function (resolver, fullfill, reject) {
fullfill('foo');
//or
resolver.fulfill('foo');
}); (where resolver has properties for fulfill, reject, reportProgress, cancellationToken etc.) That way if you just need fulfill, you could do something like: return new Promise(function (_, fullfill) {
fullfill('foo');
}); |
I am fairly strongly in favor of option 1, with |
I'm weakly in favour of option 1, I also think we should only specify that first argument, so implementers can put extensions into the second argument. |
I think we’re least likely to paint ourselves into a corner with option 1. |
+1 for option 1 |
One potentially interesting observation is that with option 1, it's possible to have an identical resolver API for both a |
Just now catching up on the discussion here. +1 for option 1. |
I happened on a chance to chat with Mark Miller today. He favors: var promise = Q.promise(function (resolve) {
resolve(value);
}) Where "resolve" may play both role of the "resolve()" function and "resolver" object in the sense that any additional methods may be properties of that function. Thus MarkM also strongly favors the ability to pass "resolve" as a free function. |
Back to the dual-role To clarify, I assume that Still, as long as it has I also think it should be spelled |
MarkM is also adamant that |
To be clear, MarkM’s complaint is that var rejection = reject(new Error("Can't do it"));
fulfill(rejection).then(function (x) {
// x === rejection
}) |
Wow, that example seems perfectly straightforward to me. Rename I guess the issue is whether you are dealing with maybe-promises, or whether you know at all times whether you have a promise or a non-promise value. My experience has always been the latter. And I think most people have the same experience. Otherwise we'd all be using |
That'd work for me. Removes the temptation to make the resolver itself a promise, which Legendary does at the moment.
Yup, it's only for specific APIs that won't necessarily return a promise I bring out |
This rambling may belong in another issue, but ... @kriskowal a while back I came full circle and now tend to agree with Mark about I've had one use case that might be helped (but not solved entirely) by it. I ended up solving it in another way, though, by wrapping/unwrapping a promise that I want to flow through a promise chain. One reason it makes me uncomfortable is that function identity(x) { return x; }
// true for any x
x === identity(x);
// Create a fulfilled promise
function fulfilled(x) {
return new Promise(function(resolver) {
resolver.fulfill(x);
});
}
// Weirdness
// Assume ~~ is some magical equivalence operator for promises
// that compares the promise's fulfillment value.
// If x is not a promise, this is true
// But if x IS a promise, this is false!
fulfilled(x) ~~ fulfilled(x).then(identity); Another reason is that promises already exhibit some nice functional/monad-like characteristics (whether they are or aren't monads is another topic), and monads have the property that Yeah, I know, that's all theoretical, but there are good reasons for the identity function and for idempotence to continue to work. Of course, there may be other valid use cases and practical reasons for |
KISS. It's possible to tunnel a promise through a promise chain by wrapping it in another object. If that's all that fulfill() does (tunnel a promise), then kill it now. Is #5 a dup of this? Here are my thoughts on the promise constructor: #5 (comment) |
Comments from @unscriptable This seems like a great way to achieve all of the desired features: new Promise(function (resolve, reject) {
resolve(promise);
setTimeout(function () {
reject(new Error('Operation timed out.'));
// reject function has its own "extension" properties
// so you could also do this (feel free to bikeshed these names):
// resolve.createRejectedPromise(new Error('Operation timed out.'));
// or this:
// resolve.createCancellablePromise(promise);
// or this:
// resolve.createSomeObservablePromise(promise);
}, 100);
}); Pros:
Cons:
Coments from @briancavalier I like this hybrid. It makes the most common operations, resolve & reject, totally obvious and easy to use, while still allowing the |
Re tunneling: I agree. I'm no longer a fan of So, right now I'm of the opinion that it's better for us just to say that the observable value of a promise will never be a promise, which, afaict, doesn't conflict with Promises/A+. |
@briancavalier |
@kriskowal Agreed, we probably can't reach an ideal for learnability :) I'm just feeling like |
@briancavalier Just in case it is not clear, I agree that we should not specify |
I still think the I haven't fully digested the anti-fulfill arguments but my preferred solution at the moment is |
@domenic Agreed: the name is confusing. I think we're all just using it out of habit right now :) People will tend to call it whatever they see us calling it, or whatever their current habit is. |
+1 to @ForbesLindesay's suggestion in this comment. Having the resolve arg be a function obviates the need for the spec to name it --it can be called I don't think we'll find a natural name for what is currently known as |
Oops, credit where credit is due correction: +1 to @unscriptable's suggestion in that comment :) |
Another interesting thing about So, I'll pose the question: Given that Too crazy? Will that simply confuse people? Maybe |
I think |
@briancavalier From the perspective of a spec, that does kind of make sense. Define the minimum functional requirement. Note optional, especially common/conventional features or signatures. Or maybe even spec them as "extensions". That said, it does seem to amount to choosing between defining the If there are other reasons to specify non-pending promise creation, then I'd be in favor of "core resolver spec" defining If the only reasons for spec'ing non-pending promise creation are nice-to-haves, then it's more of a toss up. Defining the |
I'm not entirely clear, but is the suggestion not specifying any way of creating rejected promises? That seems unfortunate. |
@domenic No, that's not the suggestion. My point was simply that a single function as the resolver API, call it So, as @lsmith pointed out, that raises the question of whether we should specify creation of already-rejected promises. If we do that, then perhaps our Resolver API should be a single Either way, yes, we need to specify some way of getting a rejected promise. |
@briancavalier Furthermore, including a
Which seems avoidable (in the "core" resolvers spec if there were such a thing) by not including It's likely that the rejection questions will need to be answered anyway, since technically today
is possible. This may be an issue for the promises spec, which states in 3.2.6.2
"exception" is not defined in the spec. |
Specifying |
@lsmith, perhaps it could be more clear that "the thrown exception" implies "otherPromise" in the expression "throw otherPromise". It most certainly is mandatory. |
@kriskowal what exactly is mandatory? Should the promise's rejection reason be |
When we add things like long stack traces, creating a promise can start to be costly in performance terms (if there's no need to do so). As such I'm in favor of having a way to transition a pending promise into a rejected state without creating an extra promise just to pass to resolve. In addition I find adding the convenience method to create an already rejected promise by creating a pending promise and then rejecting it much more natural than creating a method for rejecting a pending promise by creating a rejected promise and passing it to resolve. |
The problem with that is that a typical web request might look like: function get(url) {
var req;
return new Promise(function (resolve, reject, progress) {
req = http.get(url);
req.on('response', resolve);
req.on('error', reject);
req.on('progress', progress);
}, function onCancelled() {
req.cancel();
});
} This scoping issue is kind of ugly. If you go for the other alternative you might get: function get(url) {
return new Promise(function (resolve, reject, progress, onCancelled) {
var req = http.get(url);
req.on('response', resolve);
req.on('error', reject);
req.on('progress', progress);
onCancelled(function () {
req.cancel();
});
});
} This looks a lot cleaner to me, since you don't have to artificially hoist the |
I'm not personally bothered too much about raising the scope of Another idea above proposed having a third parameter ( |
If we go down the route of #3 then we need to decide what arguments get passed to the function. If people end up going different ways on this we'd have no interoperability, and this wouldn't be easy to change in a backwards compatible manner.
Do people prefer:
(where resolver has properties for fulfill, reject, reportProgress, cancellationToken etc.)
or
(the order of these arguments is arbitrary now, but could never be changed once decided)
or
(where options has properties for reportProgress, cancellationToken etc.)
The text was updated successfully, but these errors were encountered: