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
Draft D #5
Comments
The advantage of this over Draft C is that this avoids the creation of an extra (frequently wasted) promise. |
I must admit that I don't understand the purpose of asap(). Unlike then(), it does not create a new promise and the return values of the callback functions will also be ignored. But the callback handlers will be called asynchronously, right? So how is this a synchronous extension? |
That's the point: synchronous value retrieval should be as difficult as possible, to discourage it. An API's ease of use should be proportional to how often it is expected to be used, and synchronous value retrieval is rare. |
I agree with @domenic. If we do end up specifying sync inspection, it should be entirely un-ergonomic. It should make you think twice before doing it. |
@timjansen It calls its callbacks synchronously if it's already resolved (fulfilled or rejected) thus allowing synchronous inspection. The following works fine: Q.isPending = function (promise) {
var isPending = true;
promise.asap(function () { isPending = false; },
function () { isPending = false; });
return isPending;
};
Q.isFulfilled = function (promise) {
var isFulfilled = false;
promise.asap(function () { isFulfilled = true; });
return isFulfilled;
};
Q.isRejected = function (promise) {
var isRejected = false;
promise.asap(undefined, function () { isRejected = true; });
return isRejected;
}; It's doesn't return a promise (unlike then) for 2 reasons:
If you're doing assimilation you don't need the synchronous resolution. I've made a deliberate attempt to discourage the over-use of The following won't work: doSomething()
.asap(function (res1) {
return doSomething(res1);
})
.asap(function (res2) {
return doSomething(res2);
}); And to discourage polling (which wouldn't work well with any of the solutions). I don't have a problem with giving it an alternative much longer name if that would be preferred, but I do think synchronous inspection is needed. I'll talk more on that in #3 though. We can debate how much it should be used along with motivation for it. |
just a thought. It may as well return whatever the on* function returns. That way you can just reuse an identity function if you know for sure the promise is already fulfilled. |
@ForbesLindesay Thanks. I see, for synchronous polling it is definitely un-ergonomic. Unless, of course, you provide a method like Q.isPending() :) But couldn't you implement isPending() like this, using then() instead of asap()?
The only difference between asap() and then() that I can see is that asap() is a tiny little bit faster, depending on the Promises implementation's overhead, because asap() does not need to create a new promise and evaluate the handler's return value. So in my understanding, synchronous inspection would be a a feature that improves the performance of polling somewhat, but does neither extend functionality nor provide convenience. |
@timjansen according to the promiseA+ spec |
@jkroso thanks, I see, my isPending() version does not work because then()'s callbacks are called from the event loop.. |
@jkroso as I understand it the only thing is the fact that it makes that kind of code more consistent. promise.then(function () {
//assume thing has happend
});
//do thing
promise.then(function () {
//assume thing2 has NOT happend
});
//do thing2 Both the above can have weird bugs in the sometimes async case. The overhead, as long as you're creating relatively small numbers of promises, tends to be minimal in comparison to the time saved debugging those issues. As for making asap return the values that's specc'd in Draft C (#4) so best not to discuss too extensively here. In brief:
|
@ForbesLindesay thanks for helping me out again! I guess it is just a case of my scales being weighted differently though unless someone else has something to add. @timjansen has the same intuition as me at least which makes me feel less weird. lol sorry tim |
I should say that Draft D remains my firm favorite, I think we should encourage users to use |
+1 from me too. Though since it really doesn't matter what |
|
Argh wait I can't close this O_o @briancavalier or @ForbesLindesay mind doing the honors? |
OK, I'm starting to feel bad about trying/asking to close this, so that probably means it's the wrong thing to do. Request withdrawn. I don't want to be the guy squashing discussion by closing issues. (Or, in this case, impotently trying.) Still, I think In short, there is no gain from aping the |
The advantage is in the code for something like all, imagine we have an all method that only accepts promises (not values): function all(arr) {
return new Promise(function (resolve, reject) {
var pending = arr.length;
for (let i = 0; i < arr.length i++;) {
let method = arr[i].asap ? 'asap' : arr[i].done ? 'done' : 'then';
arr[i][method](onFulfilled(i), reject);
}
function onFulfilled(i) {
return function (val) {
arr[i] = val;
if (0 == --pending) resolve(arr);
};
}
if (0 == pending) resolve(arr);
});
} Sharing a signature with function all(arr) {
return new Promise(function (resolve, reject) {
var pending = arr.length;
for (let i = 0; i < arr.length i++;) {
asap(arr[i], onFulfilled(i), reject);
}
function onFulfilled(i) {
return function (val) {
arr[i] = val;
if (0 == --pending) resolve(arr);
};
}
if (0 == pending) resolve(arr);
});
}
function asap(promise, cb, eb) {
if (promise.inspect && "fulfillmentValue" in promise.inspect()) {
cb(promise.inspect().fulfillmentValue);
} else if (promise.inspect && "rejectionReason" in promise.inspect()) {
eb(promise.inspect().rejectionReason);
} else if (promise.done) {
promise.done(cb, eb);
} else {
promise.then(cb, eb);
}
} |
Your second code example has the benefit of not exposing a dangerous |
@ForbesLindesay TBH, I want people to have to jump through that extra, ugly hoop. I want a synchronous inspection to look like brute force, synchronous code, and not like |
I don't see enough support for this approach currently, so I'm closing as per @domenic's request. If we find reasons to reconsider it, we can reopen or create a new draft if that time comes. |
@dominic Can you explain? Its not clear to me what your baseing this off.
What would be the advantage of async throwing over a sync throw? |
Sure. You guys seem keen to use
Consistency: if the promise is not already fulfilled or rejected, you're going to have to async-throw anyway. |
Ok I think you guys should start calling Promises/A+, Futures or something. Since the word promise is always going to be interpreted as a metaphor for what they do. If I ask for something at a shop and they they refused to give it to me right now because the next customer might have to wait for the item to be ordered in ... well thats silly. And while they are being called promises the logic you guys describe seems silly too.
Again I can't see what you see. Can your explain how async preserves integrity? |
Promises have a long and storied history in JavaScript and in other languages as asynchronous control flow primitives. We go by that, not by any real-world analogies. Sorry to disappoint, but your unreliably-synchronous things need to be the ones going by a different name. I'm done with this thread; feel free to have the last word. |
@domic sorry just trying to get something more out of the guys who work with promises the most. Nothing I've got has made any sense. |
No, not at all. Frankly I am still trying to find out how the proposal(s) can help me, and performance is just the only benefit I have found so far. At least as long the extension is optional. |
Right then, sorry to be a bit short with you both. I apologize. As to why the effects of promises should always become asynchronously apparent, instead of sometimes-synchronously, see promises-aplus/promises-spec#4, including the links therein. The reason for this proposal will become more apparent when we fill out #3, but in short: there are specific rare use cases for performance, interoperability, and even debugging that would be helped by synchronous inspection. They are rare and intended only to be used by library authors. For example, if you are writing a templating library (or perhaps a "model" library as part of a MVC trio), you may wish to support promises as attributes of your template data. In many cases, the promises will already be fulfilled or rejected by the time you want to render things to the DOM. If so, it's much better to insert the data at the same time as the initial render, than to re-render once for each time a template attribute becomes fulfilled or rejected, or to find the location of the metamorphs you inserted and update each of them. As another example, certain convenience methods, like Finally, certain utility methods, like |
@domenic Thank you for your explanation, it helped me understand the reasoning! |
Promieses/A+ Extension: Synchronous Inspection
This proposal extends the Promises/A+ specification to cover synchronous inspection of a promise's fulfillment value or rejection reason.
It is not expected that all Promises/A+ implementations will include this extension. If the features of this extension are desired, you should test for them:
Motivation
TODO
Requirements: the
asap
methodA promise's
asap
method accepts two arguments:onFulfilled
andonRejected
are optional arguments:onFulfilled
is not a function, it must be ignored.onRejected
is not a function, it must be ignored.onFulfilled
is a function:promise
is fulfilled, withpromise
's fulfillment value as its first argument.onRejected
has been called.onRejected
is a function,promise
is rejected, withpromise
's rejection reason as its first argument.onFulfilled
has been called.asap
may be called multiple times on the same promise.promise
is fulfilled, respectiveonFulfilled
callbacks must execute in the order of their originating calls toasap
.promise
is rejected, respectiveonRejected
callbacks must execute in the order of their originating calls toasap
.asap
returnsundefined
and allows errors to be thrown synchronously.Note
This deliberately makes the API for synchronous inspection similar to that of asynchronous inspection to discourage anti-patterns like polling for completion. It is also optimized for the common use case of a promise that may or may not be completed already.
The text was updated successfully, but these errors were encountered: