-
Notifications
You must be signed in to change notification settings - Fork 165
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
onFulfilled not a function/onRejected not a function #25
Comments
Having |
Yes, in general, but it would be throwing in a miss-use case, which is different to throwing in a more dynamic way. I don't think it's ideal to ignore things like: getDataFromServer().then(5, true); I think the promise library should be allowed to throw an exception in that case. It would be wrong to throw a syncronous error with: getDataFromServer().then(function () {
throw new Error();
}); because its impossible to trap, and likely to occur at runtime, rather than in debug/development. |
Hmmm, right, failing early and loudly during dev is a good thing. The spec currently says they must be ignored if they aren't functions. In when.js we do check to see if |
No, I really dislike this idea. I never want to see an error from More philosophically, argument type checking is not "the JS way," for better or for worse. |
I tend to prefer "help the developer" over "the JS way", when they don't happen to intersect. I do agree that a non-compliant "debug mode" can help here. However, if a library author decides to make Or maybe coming at it from another angle: How would a developer track down a problem that occurs from a mistake where he/she passes in something that's not a function, if |
Generally I agree, but:
To answer both, I feel that we should specify that
The same way they track down passing a function to I'm willing to be overruled on this but would like some other parties to chime in. The phrasing I'd possibly be OK with is "3. If |
I agree that Naturally vars will be passed into Introducing thrown errors means added complexity when sharing promises across systems (which may be inevitable, but having a spec on your side is good). I'm usually all for making the dev's life easier, but in this case, I agree with @domenic that |
I considered this, too, but the purpose of A+ is to fill in the missing details of A. We're already leaving out |
@lsmith even though it's essentially a "compile time" error? i.e. in a typed language, a developer would almost always find this error before any code even executes. Just to be clear: I'm not suggesting that we require type checking in the spec, I'm just wondering if we should word the spec in such a way that it's not disallowed, such as @domenic's suggested phrasing. |
@lsmith can you explain in a bit more detail what you mean here? |
@domenic: If you are promoting "the JS way" -- and "the JS way" means to fail obscurely -- I don't know what to say next. :) I understand why a native function, like parseInt(func), may not cause a run-time error until some time in the future (or never at all). However, others that can't reasonably be expected to coerce to something reasonable, don't:
|
Right, and the Array iterators are actually spec'd that way. They must throw. Again, I'm not advocating that we require it, just that it seems harsh to prohibit it. It's so clearly a programmer error that it just feels wrong to force an implementation to eat it silently. |
Errors will occur during dev or in production. In either case, the crux is the assignment timing and values given to
I think the nature of the API is such that args will be passed with certainty, and the likelihood of args being passed with less certainty won't gain significantly from thrown errors in a way that doesn't jeopardize errors being thrown in production.
This is a more compelling point. If the goal of the spec is to model an API that could be adopted into native ES, it makes sense to follow that precedence. I'm now less firmly in the "no throw" camp, but still leaning that way.
Glad you asked. In thinking on it more, I can't come up with a useful example case. There may be one/some, but none are coming to mind now. |
@unscriptable @briancavalier comparing to |
An example of optional function treatment is in Others? |
I think one difference here is that, even though they are both optional, a developer will almost always supply at least one of
Yes, I'm saying that my current favorite option here is not to prohibit an implementation from sync throwing for a truthy, non-function value. Interestingly, I just re-read Promises/A, and it does dictate that any non-function arg must be ignored:
And to be fair, I can't remember hearing of anyone getting bitting by this mistake using when.js, so it's possible that it's just not the kind of thing that trips people up. Or, as @lsmith put it:
Hmmm ... more thinking required ... |
I'm curios as to what other implementations do about this in the wild. What does Q do for example? I'm not saying we should necessarily force libraries to do type checking, I'm saying that we probably should leave that behaviour unspecified in the case of 'truthy' non-functions. As I see it there are three main things that need to be achieved by promises-aplus:
I think it's fair to say that it would be very bad practice for any promises library to call then with a 'truthy' non-promise (see my earlier example). So I don't think 1 is an issue here at all. Point 2 is then up for debate. The values sent to then should always be fairly consistent, it would be very bizzar to send user input straight to the then method: //please don't do this
getDataFromServer().then(eval(document.getElementById('inputID').value)); so if it's going to fail, it's going to fail at development time and it's going to fail consistently. I think this is the most likely expected behaviour from a users point of view. If I pass the array In terms of point 3, I think that we should consider what current implementations do. If most ignore all non-functions, we should probably go ahead and require them to do so, making all promises more consistent. If we don't though, we should probably take it that ignoring just falsy values is the more natural way to go, so we should probably go with that. |
Interestingly, jQuery seems to swallow strings for optional function arguments in jQuery.ajax. I suspect this is just an issue people don't often notice. Args tend to be passed with certainty and it tends to be obvious what sort of arg should be passed. |
Ugh, GitHub closes if you push to a non-master branch too. This is still open. |
In my opinion, under the "don't leave things unspecified" school of thought, we have two options:
Let's take a quick straw poll:
My answers of course being "yes" and "1," but I could live with 2 if @kriskowal can. I'd also be curious if TC39 has an opinion; perhaps if we can't reach a consensus we could ask es-discuss. |
My vote: no and 2. I'd like to propose:
I think we should see if any de-facto standards emerge, and then we can specify this in promises/A++ |
my straw: no and 2 |
Yes and (1), though I'm not 100% convinced. If it were to be (2), then I'd add non- |
I was just about to jump in and counterargue with @ForbesLindesay, but I like the non-argumentative stating of opinions the straw poll has produced. Let's say we wait until 17:30 UTC (i.e. 1:10 from now) to let things settle, both here and in our respective heads, before wading back into things :) |
yes* and 2 *I could live with this since it does feel very ES-ish like Domenic points out, but I could also be swayed to allow certain other falsey values in as well. |
I don’t mind mandating that |
@kriskowal I understand, and we're in a similar situation in when.js. There is usage in the wild, and it's shown in our docs. There's no doubt it would be a pretty big deprecation and require a major version bump to 2.0, but it's not impossible. We could do it. Q isn't yet at 1.0, so it seems like if there were ever a time to do this, it would be now. It would be good to hear from @wycats and @lsmith on this, as well. |
It's not like we'd need to start throwing immediately, we could have code like: if (typeof callback != 'function' && tyepof callback != 'undefined' &&
typeof console == 'object' && typeof console.warn == 'function') {
console.warn('Passing ' + JSON.stringify(callback) + ' as a callback is deprecated, pass undefined instead.');
} I still really don't think this is something that is needed to allow promises to interoperate properly. I think we should be keeping this open for the future. If we don't spec this behaviour yet, we could spec it in the future, and not break backwards compatibility, because functions like I think we're beginning to get to the point where the discussion is going round in circles though, and the issue needs resolving. I think either we should have a vote (perhaps just of the n-most popular promise library's maintainers) or try and get someone with spec writing experience (from TC39??) to give some advice. |
Perhaps we should also try and involve the people on esdiscuss |
I'd say that we've made progress, and we've narrowed to a very specific question: Should we allow IMHO, it's a very good thing that our process so far has been light weight. If we could get focused advice and additional perspective on this particular issue, I think it would be helpful. |
As a newbie to promises, I would prefer that an implementation be allowed to not ignore non-functions:
I argue against having a "non-compliant debug mode" because that means that programs written correctly against the spec will fail in the debug mode. Consider this scenario:
If an implementation is allowed to respond to non-functions, then I also have a preference that it be allowed to have
On the subject of
I would in fact suggest that |
FYI Dojo's implementation invokes the callbacks if they're truthy: it assumes they're functions. Any error in invocation (including invoking a truthy non-function callback) causes the returned promise to be rejected. |
Sorry for my absence, I was out of town. For @domenic's poll, yes and (1), but barring that I could accept either (2)+
I would agree that For another implementation reference, Crockford's Vows (https://github.com/douglascrockford/monad/blob/master/vow.js) ignores non-functions. |
I find it very strange people are trying hard to standardize on something that not a single current implementation does. Are you all that unhappy with the current de-facto standard of ignoring non-functions? |
I'm not that fussed given the options of 'throw on non-functions' and 'ignore non-functions'. I just think it's a shame that we're prohibiting clever things as extensions: A short-cut for returning constant values (except /**
* @return {Promise<bool>}
*/
function saveToServer() {
return post('http://foo.com', data).then(true);
} A version that parses strings that look like ES6 style function getJSON(url) {
return get(url).then('(data) => JSON.parse(data)');
} I accept that this doesn't seem to match that many people here's opinions though, so I'm willing to back down and accept whatever consensus can be reached on this. |
@ForbesLindesay I totally agree that's useful functionality, but imho, it's the kind of thing that should be provided as a separate method, not as an extension to |
Is a concern that without real-world experience with the proposal, it may be risky to mandate it in the specification?
Oh no, I didn't mean to disparage using optional arguments. |
My position: Q will always accept null, undefined, and functions as arguments to then. I might make it throw an error if you provide something else, or I might leave it as-is, in much the same way that I might make it throw an error if you provide seven arguments instead of one, two (or three). |
Returning to the origin of A+,
Without risking the language in that sentence, ignoring non-functions is de facto, and doesn't seem to have proven problematic. Adding a throw in the spec means existing implementations will need to be updated or ignore the spec. I want broad compliance, which hinges on implementers being willing to accept the costs of compliance. Zero cost is ideal. This would introduce a cost to all implementations. |
I fully respect the desire to go with de facto behaviors. It certainly provides the least amount of friction for existing implementations. As I said over in #32, if the other implementors here agree it is the right way to go, I will reluctantly go along. However, I don't think de facto behavior can be the only concern. The fact is the debugging promises is painful, especially for those new to promises to whom something like this is more likely to happen, and I see this as a chance to help that situation, even if only slightly, and improve upon Promises/A. IMHO, we need to consider both developers and implementors. All of that said, I think it's very important for this spec to keep moving and reach 1.0 sooner rather than later. Based on the most recent comments about
So, let's have another straw pull. 1 or 2. |
2 |
1 |
(3) Must accept |
1 |
1 if 3 does not fly. |
3, and failing that 1 |
Ok, it seems that 2 does not have enough support. The most viable options seem to be:
Of those, 1 seems to be the most acceptable since the 2 votes for 3 had contingency clauses :) The current spec language is, afaict, sufficient for 1. If you absolutely, positively cannot live with 1, reopen this issue. |
But @briancavalier, what is your vote? The tally currently stands: 1: domenic, lsmith 2 lost in the first round, but you're still entitled your vote in the run-off election :) As a member of the public, I would wish to hear more arguments for why to chose 1 over 3. I've heard the argument that |
Thanks @awwx :) To be completely honest, though, 1 and 3 are a toss up for me. That sounds like a cop-out, but if "must throw" is not an option, then our plan for when.js will be to provide the best possible developer feedback via when/debug while staying reasonably within the boundaries of the spec. If @kriskowal or @ForbesLindesay do feel strongly enough about 3, then they should certainly reopen this. I'm ready to move on, tho. |
I think the only sane option is 2. It is the most javascript-like and is the most friendly to devs. Not gonna drag this out, though, if everybody wants to be insane. :) Fwiw, I just found another optional function parameter that throws, although this is in an ES7 proposal: http://wiki.ecmascript.org/doku.php?id=strawman:data_parallelism#scatter (see conflictFunction) Like @domenic suggested earlier, it only accepts undefined or a function, throws on everything else. |
It seems for points 3 and 5 it seems that it would be reasonable to only ignore 'falsy' values and to throw on other non-functions?
The text was updated successfully, but these errors were encountered: