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

Done #43

Closed
ForbesLindesay opened this issue Nov 15, 2012 · 59 comments
Closed

Done #43

ForbesLindesay opened this issue Nov 15, 2012 · 59 comments

Comments

@ForbesLindesay
Copy link
Member

Could we define the behaviour of the .done function? I really think all promises implementations should include this method, and it would be great if they were consistent.

I don't know if it belongs as part of this spec, or a separate spec, but I want to be able to say "The object has a .then method so it is a promise. The promise has a .done method, so .done will ensure errors are thrown."

@briancavalier
Copy link
Member

I'd prefer not to include .done. I feel strongly that we should keep the spec as minimal as possible and focused on .then.

@domenic
Copy link
Member

domenic commented Nov 15, 2012

Agreed. This is outside the scope of

This proposal attempts to clarify the behavioral clauses of the Promises/A proposal, to extend it to cover de facto behaviors, and to omit parts that have proven to be underspecified or problematic.

Maybe we can include done as part of a larger error handling spec, along with @wycats's desired unhandledRejection hook. But that's medium-term future.

@domenic domenic closed this as completed Nov 15, 2012
@medikoo
Copy link

medikoo commented Nov 19, 2012

I think something like done is very important, when just using then we mute any unhandled exceptions, and I'd say it's unacceptable in serious development.

Each promise implementation should have a function that escapes a chain and frees unhandled errors, done looks as right one to demand. From my point of view it should have same signature as then but: 1) Must not return anything, 2) Must throw in case of error in callback or in case of error in promise chain if callback for failed flow was not provided.

I explained my reasoning also here: http://mozilla.6506.n7.nabble.com/Promises-td101158i20.html#a101162

@domenic
Copy link
Member

domenic commented Nov 19, 2012

@medikoo Believe me, I completely agree with you. That's why I implemented exactly that in Q :). But as I said, outside the scope of Promises/A+.

@medikoo
Copy link

medikoo commented Nov 19, 2012

@domenic I think it should be part of Promises/A but it's ok, I take your answer :)

@ForbesLindesay
Copy link
Member Author

My reasoning for wanting it in the spec, as opposed to as a separate thing, is that we'd use it in libraries all the time if we could depend on it:

function all(promises) {
  var def = defer();
  var arr = new Array(promises.length);
  var waiting = promises.length;
  promises.forEach(function (promise, i) {
    promise.done(function (val) {
      arr[i] = val;
      if (--waiting == 0) def.resolve(arr);
      }, def.reject);
  });
  return def.promise;
}

That bit of code is typically written using .then because that's the only one we've specc'd,but it's much safer to write it with done, as that'll throw properly if there's an error in my all implementation or in my deferred implementation.

I'll accept it as not part of this spec, but I think it should be part of the near future. I personally think we should aim to specify as many of the methods that often appear on promises implementations as possible, one at a time in separate spec's.

@juandopazo
Copy link
Contributor

In the light of Firefox implementing done(), should we reconsider adding it to A+ to pave the way for DOM/JS specs? It would also help as get the right behavior we want from the standards groups. For example, Firefox's done() doesn't throw when a rejection callback is omitted.

@domenic
Copy link
Member

domenic commented Jul 17, 2013

Firefox has deviated from the DOM promises spec in several ways; I think they're just lagging.

@juandopazo
Copy link
Contributor

I think you're right.

But theoretically, if we were to agree on something, would we agree on this (expressed as if part of the DOM spec)?

The done(fulfillCallback, rejectCallback) method must run these steps:

  1. If fulfillCallback is not omitted, append fulfillCallback to the context object.
  2. If rejectCallback is not omitted, append rejectCallback to the context object.
  3. Otherwise, run these substeps:
    1. Let throwCallback be a JavaScript Function Object which when called runs these steps:
      1. Let reason be the first argument that is passed, and undefined otherwise.
      2. Throw reason.
    2. Append throwCallback to the context object.
  4. Return undefined.

@domenic
Copy link
Member

domenic commented Jul 19, 2013

Ugh, I hate reading DOM spec writing...

I don't think that's correct. It doesn't handle the case where fulfillCallback or rejectCallback throws.

Previous discussions at promises-aplus/unhandled-rejections-spec#5.

@juandopazo
Copy link
Contributor

In the context of the DOM spec, it does because it doesn't create a "promise wrapper callback" for each callback, which would catch exceptions. It just add callbacks to each callback list as they are.

@briancavalier
Copy link
Member

You guys know how I feel about done. I'm still opposed to it. Based on the work in when.js's new promise monitor, it seems entirely possible to detect, and even rethrow uncatchably if you want, unhandled rejections without making developers decide whether then or done is the right method to call.

@ForbesLindesay
Copy link
Member Author

I'm largely in favour of done as I think everything else ends up being too vague and losing information (You lose loads of info when streams have unhandled error events)

I would specify it as

  1. promise.done(...args) is equivallent to [[Done]](promise.then(...args))
  2. [[Done]](promise) is an abstract operation with the following behavior:
    1. If promise is eventually rejected with e:
      1. Return undefined
      2. throw e in a new execution context/in the next tick
    2. Otherwise return undefined

@juandopazo
Copy link
Contributor

@ForbesLindesay that creates an unnecessary intermediate promise and it forces an extra tick which may not be necessary. But I guess we agree that it must throw if no reject callback is passed and it returns undefined.

I'm looking at the unhandled rejections monitor in when to see if it really solves the issue.

@wycats
Copy link

wycats commented Jul 20, 2013

The approach we're taking in RSVP is to install an unhandled promise monitor that throws by default.

You can opt a particular promise out of this behavior by attaching a noop failure handler, if you know that you will be attaching asynchronous error handlers. We will probably have sugar for this (.undone :p)

In our experience, moving the burden from literally everyone to people who may want to attach async error handlers is appropriate.

@medikoo
Copy link

medikoo commented Jul 20, 2013

@domenic @briancavalier @wycats

There are few very common use cases, that I think must be addressed by promise spec:

  1. A need to process final result of some asynchronous process.
  2. A need to access asynchronously resolved value.

Those uses cases are in a dead simple and straightforward way solved by done of which implemetation is simpler than then and can be easily specified.

Instead you propose that official specified solution for that should be using then, where:

  1. then affects performance doing extra obsolete work (by mapping value A to not needed value B).
  2. Additionally it introduces the issue of intercepting any eventual errors that may occur when processing the result. Acknowledging that you propose a sophisticated unhandled error monitoring (and re-throw) mechanism, which is still unclear how it should work and be implemented, and I'm pretty sure will rise many other issues.

So cutting it short: instead of giving simple and possible solution A, You suggest using solution B which raises 2 important issues, and propose a complicated and not complete solution C which will just address one of them. How logical is that? :)

I totally don't buy the argument that deciding whether to use then or done is a bit too much for developer's mind.

Both are different and serve different and clear use cases. Same with array iterators, we have forEach and map, and nobody complained that we should remove forEach as it's confusing to have it with map through which we we can also iterate.

@wycats
Copy link

wycats commented Jul 20, 2013

@medikoo The way done is described today to new Promise users is "make sure to always make done the end of your promise chain."

In Ember, a common use case for promises is the return value of the model hook:

App.IndexRoute = Ember.Route.extend({
  // if the return value of this method is a Promise, Ember will wait for it to resolve before
  // moving further down the route hierarchy and before rendering any templates
  model: function() {
    return $.getJSON("/me").then(function(profile) {
      return $.getJSON(profile.photosURL);
    });
  }
});

As you can see, it is inappropriate to tack done to the end of this promise creation, because you are passing the promise along to Ember to handle. This is actually a rather common use case in abstractions: one piece of code generates a (possibly complicated) promise, and hands it off to another piece of code to handle.

In this case, it would obviously be disastrous (and broken) to change the final then to a done. This means that the explanation for when to use then or done is more subtle and involves explaining a lot more about the mechanics before people can become productive using promises. Before long, they're wishing they were back in callback-land, where everything is straight forward.

The advantage of Promises is that they create a single, straight-forward API that totally replaces the callback-based API. In my experience, people's initial brush with promises is not very sophisticated, and adding in done to the "Getting Started With Promises" experience causes serious adoption issues.

In libraries that are mostly used by people who have opted into promises and learned about them, I can easily see how adding more learning by way of done wouldn't be such a big deal. In contrast, Ember makes promises the only way of managing async, which means that people have not opted in to promises. It is crucial that people who we have opted in to promises (soon to be DOM) do not encounter gotchas that make them run for the hills and beg for callbacks.

@domenic
Copy link
Member

domenic commented Jul 20, 2013

@wycats sorry that's a really bad example. The way done is described today is through the golden rule: "always either return the promise to someone else, or call done."

@wycats
Copy link

wycats commented Jul 20, 2013

@domenic Sorry, but that isn't how people explain done. Regardless, that "golden rule" needs to be invoked literally every single time someone wants to use a promise and it's way too easy to make mistakes.

@domenic
Copy link
Member

domenic commented Jul 20, 2013

Sorry, but that isn't how people explain done.

Source?

@wycats
Copy link

wycats commented Jul 20, 2013

My proposal (and it sounds like, @juandopazo) is to keep the promise usage API simple (always .then) and require complex consumers of promises who want to attach async error handlers after the fact to opt into this somewhat rare use case.

This shifts the burden from everyone to a much smaller group of people who have a somewhat exotic use-case.

@wycats
Copy link

wycats commented Jul 20, 2013

@domenic http://msdn.microsoft.com/en-us/library/windows/apps/hh700334.aspx

The first set of rules doesn't mention your golden rule. It says:

Use then for an intermediate stage of the operation (for example .then().then()), and done for the final stage of the operation (for example .then().then().done()).

You can go get Microsoft to fix this particular phrasing, but that isn't my point. It's really hard to propagate a rule throughout the ecosystem that is both accurate (and doesn't hit the issue I raised) and doesn't fall victim to a game of telephone.

You understand promises very well, so the rule seems obvious. Remember that for most people, the idea of a first-class eventual value is very new, and they're just following simple instructions to get their job done.

@domenic
Copy link
Member

domenic commented Jul 20, 2013

We've been over this before, but to just reiterate it for the record:

The use case of attaching handlers more than one tick after the fact is not at all exotic, and is in fact crucial to the composability of promise systems and the ability to refactor them without fear. You will one day upgrade your third-party promise-accepting library and find it completely broken for "no reason," only after much debugging realizing that you now need to undone() the promise you pass in to them before letting it out of your sight.

undone is a non-local change, whereas done is a local one.

@wycats
Copy link

wycats commented Jul 20, 2013

My bottom line: done is a hack, and converts a simple API with a very easy usage pattern into a binary choice on every single usage. It is unacceptable to me.

@domenic
Copy link
Member

domenic commented Jul 20, 2013

To be clear, I still think unhandled rejections is a huge problem, and done is not a great solution. I think the solution is simple, easy-to-use debugging tools, which @briancavalier has apparently done, and @stefanpenner and I are working on independently of his efforts. That seems much preferable than a non-local undone-carrying Promises/A+-violating hazard propagating through the system.

@wycats
Copy link

wycats commented Jul 20, 2013

@domenic I am strongly in favor of tools. The undone solution is primarily a temporary hack that might get us over the hump while tools are being built. For the reasons you described, I don't believe it to be a tenable long-term solution, but (to me) it's better than a short-term solution that involves forcing a choice in the promise API on every usage.

The primitive .onerror in RSVP is a starting point towards better tools as well.

@wycats
Copy link

wycats commented Jul 20, 2013

@domenic perhaps we're closer together than we realized?

@domenic
Copy link
Member

domenic commented Jul 20, 2013

Then I think we are in complete agreement, and we'll work as fast as we can to get those tools ready for you ^_^

@wycats
Copy link

wycats commented Jul 21, 2013

@juandopazo do you mean something like onerror? Or visual tooling? It's not really up to those specs to specify how visual debuggers should work.

@medikoo
Copy link

medikoo commented Jul 21, 2013

@wycats

The way done is described today to new Promise users is "make sure to always make done the end of your promise chain."

Exactly

In Ember, a common use case for promises is the return value of the model hook:

This is very different use case, it's not end of promise chain, you actually return promise for further processing.

I advise you to look out of Ember into more direct promise usage e.g. how it's done in plain Node.js scripting, over there use case for done are very common.

@wycats
Copy link

wycats commented Jul 21, 2013

@medikoo I've, of course, used promises in many other use-cases. I understand why the (overly simple) advice is common and works for many people. My point is that if you're the author of an abstraction that requests that the user HAND you a promise, this popular advice will cause you no end of pain, because it's overly simple and doesn't capture the nuance.

Once you explain it properly, it's very hard to make sure that everyone who describes .done makes sure to include the "unless you're returning" caveat, and indeed, neither Q nor WinJS includes that description in their API docs.

Even if you did explain it properly, it now means that users have to think about this choice in every usage of promises, making a simple API (.then to attach handlers) into a choice on every usage.

I fully, 100%, understand the pain that swallowing errors causes. This has been the source of a lot of frustration by Ember users, and I agree that a solution is required. However, I believe that printing unhandled exceptions to the console in the very short term, followed by better tooling in the medium term, will solve the problem without making the API more unpleasant to use.

@ForbesLindesay
Copy link
Member Author

Just putting this out there:

done is effectively the default behaviour of C#'s Task object, which has been in very common usage for a few years now. Adding something similar to .then is a very common extension method for people to add. I have not seen a single person struggle with deciding which to use when.

People struggle because we don't teach it as:

  1. To get the value of a promise then do something use .done
  2. To transform the value of a promise and get a new promise use .then

We're teaching the two methods the wrong way round. .done is the primative, .then should be the extension.

rant start

Code is written once, maybe twice and then read tens or hundreds of times and executed thousands of times. The importance of really clear signaling of intent and early and LOUD failure cannot be overstated. No unhandled rejection debugging tool can shout as loudly as actually calling .done. Using that debuggint tool will only ever be done once you realise that "maybe it's an unhandled rejection that's getting lost somehow".

rant over, sorry

@Twisol
Copy link

Twisol commented Jul 21, 2013

@ForbesLindesay - C# also has the Reactive Extensions library (Rx) produced by Microsoft, which (to the best of my knowledge) is built on top of Task and is meant for the same kinds of things as Promises. The primitive is Subscribe, which takes an object with OnSuccess, OnCompleted, and OnError methods. I don't believe a Done method exists in Rx - or at least, the user guides I'm reading haven't mentioned it yet, and errors appear to be re-thrown automatically.

I dislike having to use done with JS promises. If an error is thrown, and I later attach an error handler, it should be too late - I missed my chance, it happened, it's gone. It ought to be thrown immediately. I've already had occasions where I properly return a promise from a function without calling done, but ignore the return value, causing those errors to be swallowed.

@ForbesLindesay
Copy link
Member Author

Reactive Extensions are not built on top of tasks, they are much more similar to node.js's Stream class. The task related methods are mostly just let you do things like wait for the next value in the IObservable (Reactive's equivalent to a readable stream).

Yes, we've all had occasions where we forgot to add a .done() handler and an error was swallowed. My point is:

  1. most people are in agreement that .undone() is not an option as it breaks having the ability to handle errors in the next tick without remembering to call .undone(). As @domenic says, it's a global change and that ship has sailed.
  2. Everyone is in agreement that it will be useful to have better tooling for tracking down the unhandled rejections that go missing.
  3. I think .done() is an extremely useful tool for improving the logging of errors and allowing them to shout as loud as possible. Note that point 1 prevents any unhandled rejection tooling from shouting loudly enough to be noticed when you aren't already looking for it while still being quiet enough to not be annoying when you're deliberately handling errors in the next tick.

@wycats
Copy link

wycats commented Jul 21, 2013

@ForbesLindesay my hunch about why it's not confusing is that "don't swallow errors" is the default in C#. If you want more advanced behavior, you learn about and opt into error swallowing.

With JS promises, we chose (for good or ill) to error swallowing the default. That forces us to find better solutions that help users avoid those hazards.

Perhaps a better initial solution would have been .then with the .done behavior, and a .chain or .pipe for the current behavior. I believe jQuery experimented with that.

Again, I think we're too far down the road to change the core semantics now.

RSVP is choosing, for now, to throw unhandled exceptions via the default behavior of an onerror hook that gets invoked for unhandled exceptions. I hope we have a better solution before @domenic's doomsday scenario comes to pass.

@ForbesLindesay
Copy link
Member Author

Of course it's too late to change the .then behavior (incidentally what you're doing with your default of throwing unhandled exceptions). That would break far too much code.

It's not too late to make .done available with the non-error swallowing behavior. It's also not too late to encourage people to learn .done first and then learn .then. I think people will pick up .done super quickly as it's ridiculously simple, and then they'll really love .done when they see how powerful it is. Having learnt them both as being first class citizens of promises, they'll have no trouble learning which to use when. Once you understand which to use when, the cognitive overhead is minimal/nonexistent.

The issue is that at the moment we're teaching people that .then is the golden panacea of promises and .done is this stopgap that's just there because tooling isn't great yet. If we let .done be a first class citizen I think it will perform the job admirably.

@wycats
Copy link

wycats commented Jul 21, 2013

.done is a kind of silly name for the main, default primitive, and making sure people get the right guidance here seems really hard.

I'm not changing semantics; just providing a hook with convenient defaults for the current situation.

@briancavalier
Copy link
Member

To get the value of a promise then do something use .done
To transform the value of a promise and get a new promise use .then
We're teaching the two methods the wrong way round. .done is the primative, .then should be the extension.

@ForbesLindesay This is a very clear explanation, and is the first time I've thought done has real value. Thank you.

My experience in seeing people use promises matches @wycats', though. I see people make mistakes with done vs. then when both authoring and refactoring promise-based code.

That said, neither done nor tooling which would be enabled by something like PromiseStatus is a silver bullet. Perhaps more importantly, though, is that they aren't mutually exclusive.

done may be able to shout more loudly in specific cases, but there things it simply can't help you with: e.g. if you accidentally call then instead. Tooling can shout loudly, but perhaps not as immediately. However, enabling tooling could have significant benefits. It can collect and visualize information that done simply cannot. It can warn about promises that appear to be pending forever. It can be created by 3rd parties, and all promise implementations and their users can reap the benefits. And once tooling is enabled, it can be created external to the promise implementation, and so adds no code bloat or maintenance burden to the promise implementation.

@medikoo
Copy link

medikoo commented Jul 22, 2013

I really doubt, that it's confusing for developer to learn when to use done and then. If it is, it's only because we learned and forced them to use then for everything, and we need to take that back, but it's our fault not theirs.

Difference between done and then is simple, then means continuation, done means end. If devs are able to distinguish forEach and map on arrays, they will also have no trouble with promises API.

@ForbesLindesay
Copy link
Member Author

@medikoo amen, you arely see people mistakenly using .map when they want .forEach or for (var i = 0;..........

Think back, did anyone learn to use array.map() before they learnt either for (var i = 0; i < array.length; i++) or array.forEach. I know I didn't, and it made .map() into this really awesome tool that I use exactly when it's helpful.

@briancavalier I agree with all of this. I think we would all do well to remember "they aren't mutually exclusive". I still think the problem .done has at the moment is solvable via better laid out tutorials and I'm concerned that if the additional tooling has to be enabled in some way (realistically this is going to be true for the next few years at least) new devs won't enable it straight away no matter what you tell them, and if they don't have .done that will mean silenced errors.

Conclusion

I think we should have a separate repository for a "Promise Chain Termination" spec. By separating out the specifications for .done (or something similar but with a better name?) and the specifications for unhandled rejection tooling we can encourage people to see that they are not mutually exclusive. If we do this, I think both have a much better chance of producing something useful.

@ForbesLindesay
Copy link
Member Author

P.S. @wycats you are leaving it as possible to opt-in to Promises/A+ semantics by replacing the default error handlers, but your default behavior is not Promises/A+ compatible. This is why you are changing the semantics.

@juandopazo
Copy link
Contributor

I'm against another repository for this. I think we already agree on the semantics of done and that most implementations already follow it. That is: done returns undefined so it doesn't map to anything, it doesn't wrap callbacks to catch errors and it notifies asynchronously just like then.

@wycats et al, my point previous point about DOM promises and debugging was that I don't see DOM promises having any sort of monitor for unhandled promises, so done would be a way to get something to help with debugging. Otherwise we will end up assimilating DOM promises instead of using polyfills. I've seen some discussions about monitoring unhandled promises in es-discuss, so it's possible we'll get something like that from Ecma, but I don't see the DOM implementing anything like it. And I think it would be much easier for us to go to the WHATWG asking for done than asking for a much more complex strategy.

@briancavalier
Copy link
Member

using .map when they want .forEach or for (var i = 0;..........

Interestingly, I find the opposite to be surprisingly common: I see people using forEach or for, when map or reduce is the operation they are actually trying to perform. They reinvent map and reduce using forEach. Honestly, I almost never use forEach. I would guess that 90% of the time or more, I use map or reduce, and I don't want to throw away the result by using a purely side-effectual forEach.

Perhaps this means that if done had existed before then, devs would be using it all over the place and jumping through hoops to simulate then. And if, at some point later, then had come along, they would continue to misuse done until they saw the light of then.

I'm not really sure where this leaves done in my mind ... maybe in exactly the same spot as forEach, at least for me: the right fit for an extremely small number of cases.

@ForbesLindesay I think the "devs won't enable tooling" problem could be solved in much the same way as teaching devs how to use done vs. then: education and mistake-making will be required.

I don't see DOM promises having any sort of monitor for unhandled promises

@juandopazo Do you know what plans they do have for helping developers to debug? It would def be interesting to know.

@ForbesLindesay
Copy link
Member Author

I do think we will see developers jumping through hoops to get the .then behavior. On the other hand, I think that's very good preparation for discovering .then and later understanding how it works. I've seen way too many people write code like:

function getJSON(url) {
  return new Promise(function (resolve, reject) {
    get(url).then(function (res) {
      resolve(JSON.parse(res))
    }).then(null, reject)
  })
}

which is just painful, I'd rather people started out with .done and thought "hey, separating inputs from outputs is really useful and so is being able to wait for an array of things to be done" and then after a bit longer thought "the code needed to transform one promise into another is a bit verbose, I'll write a helper function that takes a promise and a mapping function", then a little later thought "I'll publish my helper method on the internet, oh wait, there's this .then thing built in to promises, that's really useful".

That seems to me like a natural learning curve. At each stage, the tools you get seem better than what you had before with few obvious downsides. I think a lot of people will get put off by .then's error swallowing behavior and never come back.

@juandopazo

If we all (roughly) agree on how .done should behave, doesn't that make it the perfect candidate for getting a repository and a spec and then some unit tests?

@bergus
Copy link

bergus commented Jul 22, 2013

make sure to always make done the end of your promise chain, for the final stage of the operation.

While that may be a good rule of thumb for new promise users, we shouldn't think of chains of promises only. Since multiple handlers can be attached on a promise, I like to think of them as trees (basically we could build all kinds of acyclic graphs) and done would be some kind of "leaf handler" which can be child of an inner node. It does not necessarily mark the end of a chain, though.

@juandopazo wrote:

I think we already agree on the semantics of done and that most implementations already follow it. That is: done returns undefined so it doesn't map to anything.

I would like done to return the input promise actually, to enable method chaining on it. jQuery does this for example, and I find it pretty helpful to use it like Underscore's tap:

function getJSON(url) {
    return get(url).done(console.log.bind(console, "original response:")).then(JSON.parse);
}

@juandopazo
Copy link
Contributor

@bergus I know that chaining is standard practice, but IMO in this case returning anything other than undefined makes it even more confusing. Returning undefined clarifies that you're not mapping the promise and removes the need to think about what the callback should return. It documents itself.

@juandopazo
Copy link
Contributor

BTW it looks like Firefox removed done.

captura

@medikoo
Copy link

medikoo commented Jul 22, 2013

@bergus Calling done, means we're done and we do not extend or pass promise value for further processing.

What you're asking is something between then and done which I believe should not be covered by spec, but individual libraries may provide sugar for that, e.g. in Deferred (lib I maintain), I have aside extension which does exactly this.

@wycats
Copy link

wycats commented Jul 22, 2013

@briancavalier that is also my experience.

I teach an introductory Ember course for people with jQuery and JS experience, and I see a lot of confusion about map vs. forEach. This may make you sad, but it is the reality.

@briancavalier
Copy link
Member

I like to think of them as trees

@bergus Yep, me too. We should probably use "tree" when referring to a promise graph/subgraph rooted at a particular starting promise, and "chain" to mean a single path from said starting promise to a leaf (which does indeed have an end ... at the leaf!).

I see the utility in making done be tap-like. However, I agree with @juandopazo and @medikoo that if done exists, it should serve the most narrow purpose possible. Returning anything remotely promise-like seems like an opportunity for beginners to confuse it with then. (as they say, things that are different should be very different)

@wycats
Copy link

wycats commented Jul 22, 2013

If we all (roughly) agree on how .done should behave

Do you mean "except @wycats"? The process of persuading a recalcitrant in order to gain consensus is useful. At the very least, it will force us to explore parts of the solution space that we've been ignoring.

@ForbesLindesay
Copy link
Member Author

@wycats yes, my comment was more about the fact that if "we mostly agree" (as @juandopazo suggested) then "it would definitely be a good time for a spec".

I actually equally think if the vast majority are settling on one thing and an implementation or two does something different, that's probably a good indication that we need to be discussing the differences and seeing what we can learn from each other.

My current thoughts are that .done should return undefined to minimize the possibility for confusing it with .then.

Also, I think @bergus example:

function getJSON(url) {
    return get(url).done(console.log.bind(console, "original response:")).then(JSON.parse);
}

would be better as:

function getJSON(url) {
    return get(url).asside(console.log.bind(console, "original response:")).then(JSON.parse);
}

which would desugar to:

function getJSON(url) {
    return get(url).then(function (res) {
      console.log("original response:", res);
      return res;
    }).then(JSON.parse);
}

I see no reason you'd want the error throwing behavior of .done?

@juandopazo
Copy link
Contributor

Yup, that's precisely why done should return undefined.

As for why I'm against another repository, it's because of how things have evolved since A+ started. Now there is almost certainty that promises will be in EcmaScript and we already have prototype implementations in the DOM. I know some people will prefer to keep their own implementations even when native ones are available, but I honestly see no reason why all of our libraries shouldn't become wrappers and utilities for native promises.

This means that the role of A+ should probably change. It's no longer about reaching consensus between web developers. Now there are two other standard bodies writing a spec for something that will be automatically available for all developers. So I don't think there's a point in having texts for optional features like progress when the native versions won't provide them.

Given that Firefox has removed done, that the DOM won't include it and that it hasn't been discussed much in es-discuss I'm considering dropping it from our implementation and I'm going to start looking into throwing for unhandled rejections.

@ForbesLindesay
Copy link
Member Author

The way I see it, the role of the Promises/A+ organisation is two fold:

  1. Encourage consensus between promise library implementers
  2. Record and document what that consensus is

Point 1 clearly becomes less important as we get native promises in the language / in the DOM. Point 2 does not.

Point 2 is partly important for users of promise libraries who want to know exactly how their library will behave. It's partly important for external standards organisations though.

The Promises/A+ method of producing a specification has proved extremely effective for producing a very useable API and a very easy to read spec (relative to other specifications). As such I think it's a great idea for Promises/A+ to continue to specify the features that most promise implementations agree on. By doing this, we can help encourage TC39 and W3C etc. to produce nice specifications that include the features people want.

@juandopazo
Copy link
Contributor

That's my reasoning too, but I'm taking an extra step: if our goal is to encourage TC39 and the WHATWG to include certain features, we should all agree on those features being included. Having optional features doesn't help get our voice heard.

@ForbesLindesay
Copy link
Member Author

ok, that makes sense, I actually kind of like the "one repo" approach. I think it would be an interesting experiment to try and get the .done method to be added to the same spec. Given that the spec is versioned, libraries that don't support done will still be able to state compatability with Promises/A+ v1.0.0 for example :)

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

8 participants