- From: Ron Buckton <rbuckton@chronicles.org>
- Date: Sat, 27 Apr 2013 16:21:43 +0000
- To: David Bruant <bruant.d@gmail.com>, Tab Atkins Jr. <jackalmage@gmail.com>
- CC: "Mark S. Miller" <erights@google.com>, "public-script-coord@w3.org" <public-script-coord@w3.org>, Mark Miller <erights@gmail.com>, Dean Tribble <tribble@e-dean.com>, es-discuss <es-discuss@mozilla.org>
- Message-ID: <33D2646B20374248B7CAA8F25919AAFC42761C3E@BY2PRD0111MB494.prod.exchangelabs.com>
Here is a case where flattening helps: function executeAndWaitForComplete(command) { return getJSON(commandUrl + command) .then(function (commandResult) { if (commandResult.complete) { return commandResult.model; } var statusUrl = commmandResult.statusUrl; return pollForCommandComplete(statusUrl); }) } function pollForCommandComplete(statusUrl) { return new Future(function(resolver) { var poll = function (pollResult) { if (pollResult.done) { resolve.resolve(pollResult.model); } else { setTimeout(function() { getJSON(statusUrl).done(poll, resolver.reject); }, 500); } } getJSON(statusUrl).done(poll, resolver.reject); }); } In this example, the server will receive a command from the client and will process it asynchronously. The client then needs to poll an endpoint to check for completion of the command. Just using Futures, flattening is helpful here. Without flattening, executeAndWaitForComplete would could return either a Future<object> OR return a Future<Future<object>>. In a non flattening world, the developer would have to change the function to: function executeAndWaitForComplete(command) { return new Future(function(resolver) { getJSON(commandUrl + command) .done(function (commandResult) { if (commandResult.complete) { resolver.resolve(commandResult.model); } var statusUrl = commmandResult.statusUrl; pollForCommandComplete(statusUrl).done(resolver.resolve, resolver.reject); }, resolver.reject) }); } With flattening, the first version is less code for the developer. With flattening its possible to run into odd errors without some kind of static analysis since JS is not type safe. Ron Sent from Windows Mail From: Tab Atkins Jr. Sent: ýFridayý, ýAprilý ý26ý, ý2013 ý2ý:ý24ý ýPM To: David Bruant Cc: Mark S. Miller, public-script-coord@w3.org, Mark Miller, Dean Tribble, es-discuss On Fri, Apr 26, 2013 at 1:39 PM, Tab Atkins Jr. <jackalmage@gmail.com> wrote: > On Fri, Apr 26, 2013 at 6:36 AM, David Bruant <bruant.d@gmail.com> wrote: >> Le 26/04/2013 14:54, Kevin Smith a écrit : >>> >>> What exactly is the controversy here? >>> >>> I think we all agree with the semantics of "then" as specified in >>> Promises/A+. (If not, then we have a really big problem!) >>> >>> If so, then the only real controversy is whether or not the API allows one >>> to create a promise whose eventual value is itself a promise. Q does not: >>> it provides only "resolve" and "reject". DOM Futures do by way of >>> "Future.accept". As far as I know, there's nothing about Q's implementation >>> that would make such a function impossible, it just does not provide one. >> >> I believe at this point the question isn't so much "can I build a promise >> for a promise?", but rather "what should be the default Future semantics?" >> Namely: >> >> Future.accept(5) >> .then(function(x){ >> return Future.accept(x); >> }) >> .then(function(y){ >> // is y a Future? >> }) >> >> I'm arguing in favor of y being guaranteed to be a non-Future value. It is >> my understanding others would want y to be a Future. >> That would be the controversy as I understand it. > > No. Future callbacks can return Futures, which then chain (the return > value of then adopts the state of the callback's return value). This > is the big "monad" benefit that we keep talking about. To lay it out even more clearly for any bystanders, in the following code: getAFuture() .then(function(x) { return doSomeWork(x); }) .then(function(y) { // is y a Future? }); The answer to the question is "no", *regardless of whether doSomeWork returns a plain value or a Future for a plain value*. If doSomeWork() returns a plain value, the future returned by the first .then() call accepts with that value. If doSomeWork() returns a future that eventually accepts, the future returned by the first .then() call waits until it accepts, and then also accepts with the same value. This all happens without recursive unwrapping. It's just a feature of how this kind of thing works (it's a result of Futures matching the "monad" abstraction). The only way that y will become a Future is if doSomeWork() explicitly and purposefully returns a future for a future for a value (in other words, Future<Future<x>>). In this case, the future returned by the first .then() call waits until the outer Future from the return value finishes, then accepts with its value, and "this value" happens to be a Future<x>. This sort of thing does not happen accidentally. It's hard to make nested futures unless you're doing it on purpose, or you have no idea what you're doing. In the first case, we want to trust the author, and in the second case, it's probably better for everyone involved if the code fails in a fairly obvious way, rather than attempting to paper over the problem. If you're competent and just doing the natural thing, the API naturally gives you plain values in callback arguments and singly-wrapped futures in return values. This is why I've been arguing against recursive unwrapping in the general case. ~TJ _______________________________________________ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Received on Saturday, 27 April 2013 16:22:33 UTC