RE: Futures

+everybody ☺

From: Kevin Gadd [mailto:kevin.gadd@gmail.com]
Sent: Tuesday, April 23, 2013 5:32 PM
To: Ron Buckton
Subject: Re: Futures

Whoops, I replied privately instead of on-list. You might want to forward your stuff to the list.

On Tue, Apr 23, 2013 at 5:28 PM, Ron Buckton <rbuckton@chronicles.org<mailto:rbuckton@chronicles.org>> wrote:

While a bit confusing, it has the upside of being explicit about what happens to your exceptions. That still doesn’t prevent developers from forgetting to “await” an “async Task” function and lose the exception.



If “await” is part of the language, I’d almost want any ignored return value that is a built-in Future to implicitly have its Future#done method called to ensure the exception is bubbled up as an unhandled exception. I can see how that can cause issues with developer expectations and would probably be too much of an over-optimization. At the very least, any kind of linter or static verification could look for a call to a function^ (if it can be resolved) and write warnings if you don’t either “await” it or call .then or .done.



A variant of “async void” might be feasible with an aforementioned Python decorator capability:



function done(fn) {

  return function() {

    var result = fn.apply(this, arguments);

    if (Future.isFuture(result)) {

      result.done();

    }

  }
}



@done function^ myAsync() {

  await someOtherAsync();

}



myAsync(); // fire and forget, any error is bubbled up to the global unhandled exception logic



Ron

From: Kevin Gadd [mailto:kevin.gadd@gmail.com<mailto:kevin.gadd@gmail.com>]
Sent: Tuesday, April 23, 2013 5:11 PM
To: Ron Buckton
Subject: Re: Futures

Thanks. Didn't know async void worked that way (gross).
-kg (mobile)
Ron Buckton <rbuckton@chronicles.org<mailto:rbuckton@chronicles.org>> wrote:
The ‘await’ keyword in C# is paired with a special function declaration (similar to function* as proposed for ES6) that can be used in one of three ways:

async void Foo() { ... }
async Task Foo() { ... }
async Task<T> Foo() { ... }

The ‘async’ keyword here flags the method for special case handing by the compiler to turn the function body into a state machine.  The kind of state machine generated is based on the return type of the method signature.

The “async void” method creates a state machine that inherently throws any unhandled exception to the UnhandledException trap/event of the AppDomain.  This is often useful for a “fire-and-forget” operation where the caller is not concerned with the result.  An “async void" method cannot be awaited by the caller as it has no return type.

The “async Task” method signature creates a state machine that returns a Task (nee Future) that will eventually signal the successful completion or the exception thrown by the function.  It is roughly analogous to a “void” method signature as it has no result, and the Task is only used for further “await” expressions or defining continuations using .ContinueWith (analog to Future#then).  As the method returns a Task, any exception from the method will not be thrown to the AppDomain and therefore should be “await”ed appropriately (alternatively, the Task.Wait() method can be called that will block the current thread until the task completes and will throw any unhandled exceptions).

The “async Task<T>” method signature creates a state machine that returns a Task<T> that will provide either the eventual return value (of type T) or throw the exception. As with “async Task”, the exception must be explicitly handled by the caller either through an “await”, ContinueWith, or Wait.

Providing a special syntax for an ES function (either function* or function^) only goes half way to solving the issues of supporting “await”, as it would require some additional special ​effort to handle Future swallowing or bubbling the exception.

I have been experimenting with an (unofficial & unsupported) fork of TypeScript that supports ES6 generators by transpiling into a state machine in the function body. Albeit slower than a native implementation, it allows for some experimentation with the syntax in any ES5 compatible browser.  TypeScript has the advantage of static type checking during compile time, which allows me to mimic the C# behavior for the “async” keyword. As such I also have been experimenting with adding async/await to TypeScript and early indicators show that its is a very convenient way of handling asynchronous development flows. An example of a async method in this implementation looks something like:

class AuthAPI {
    async getAuthProvidersAsync(): Promise {
        var providers = await HttpRequestAsync.get(acsUrl, settings);
        return JSON.parse(providers);
    }
}

class AuthView {
    providers = ko.observableArray();
    async load(): void {
        var authAPI = new AuthAPI();
        var providers = <IAuthProvider[]>await authAPI.getAuthProvidersAsync();
        this.providers(providers);
    }
}

As opposed to the non-await implementation (with Futures/Promises):

class AuthAPI {
    getAuthProvidersAsync(): Promise {
        return HttpRequestAsync.get(acsUrl, settings)
            .then(p => JSON.parse(p));
    }
}

class AuthView {
    providers = ko.observableArray();
    load(): void {
        var authAPI = new AuthAPI();
        authAPI.done((providers: IAuthProvider[]) => {
            this.providers(providers);
        });
    }
}

While this example may not show a lot of power, imagine an async polling mechanism:

class AppAPI {
   async waitFor(eventId: string): Promise {
     while (true) {
         var result = await HttpRequestAsync.get(eventUri + “?eventId=” + eventId);
         var obj = JSON.parse(result);
         if (obj.complete) {
             return obj.url;
         }
         await Promise.sleep(500);
     }
   }
}

Writing that using then is much more cumbersome:

class AppAPI {
    waitFor(eventId: string): Promise {
      return new Promise(resolver => {
        var awaiter = () => {
            HttpRequestAsync.get(eventUri + “?eventId=” + eventId).done(result => {
                try {
                    var obj = JSON.parse(result);
                    if (obj.complete) {
                        resolver.resolve(obj.url);
                        return;
                    }
                    setTimeout(awaiter, 500);
                }
                catch (e) {
                    resolver.reject(e);
                }
            }, resolver.reject);
            awaiter();
        }
      });
    }
}

Adding “await” would be a valuable addition to the language, assuming we can solve the “swallowed exception” issues, or at least call out that it is a caveat to developers that should be handled explicitly.

Another way to get async/await-like capabilities might be to combine the Q.async/task.spawn and yield functionality with something like Python-style decorators. I’ve been working on a proposal for decorators for both TypeScript and ES (and have another unofficial/experimental fork of TypeScript with support for this up on CodePlex today).  If that were the case, you could fake async/await without having to directly wrap the function call as is necessary for Q.async/task.spawn today:

class Foo {
    // example decorator using Python-style syntax (@ token may collide with symbols/private)
    @async myAsync() {
        var a = yield myOtherAsync();
    }
}

Ron

Sent from Windows Mail

From: Dean Landolt
Sent: ‎Tuesday‎, ‎April‎ ‎23‎, ‎2013 ‎6‎:‎28‎ ‎AM
To: David Bruant
Cc: Brendan Eich, Mark S. Miller, Douglas Crockford, public-script-coord@w3.org<mailto:public-script-coord@w3.org>, Norbert Lindenberg, Markus Lanthaler, EcmaScript



On Tue, Apr 23, 2013 at 9:12 AM, David Bruant <bruant.d@gmail.com<mailto:bruant.d@gmail.com>> wrote:
Le 23/04/2013 14:56, Dean Landolt a écrit :
On Tue, Apr 23, 2013 at 4:54 AM, David Bruant <bruant.d@gmail.com<mailto:bruant.d@gmail.com>> wrote:
Le 22/04/2013 19:32, Dean Landolt a écrit :

I was just saying there will be pressure to include support for thenables (already in DOMFutures). If you believe otherwise don't let me dissuade you -- I would absolutely love it if I were proven wrong!
I guess it would take making sure no content can be confused by the current steps 3-5 of the current resolve spec [1]. I believe browser vendors have tools to study this kind of things.

CasperJS [2] create objects with a then method [3]. Interestingly, this doesn't run in the browser (until someone decides to re-implement it of top of a web browser or FirefoxOS. [4] ?). Potentially more interestingly, Casper objects could be promises subclasses (to be considered).
It wouldn't be surprising if there were content on the web where the promise subclass trick couldn't work.

What do you mean by the promise subclass trick? If you mean that Casper instances would subclass Promise I don't think that'd work out too well as is. Promises/A (and I presume A+) intentionally specified `then` to return a new promise, so Casper would have to change in a very breaking way to pull this off.
I'm not sure it would be a breaking change for the vast majority of people. They may actually like that instead of being forced to to casper.start. They may also enjoy being able to chain .then-s. But maybe it's breaking.

IIRC you can already chain thens -- the break is that you will be forced to chain thens in order to maintain your intended chronology. Inheriting from Promise would suggest all those then calls will be fired in parallel instead of happening in serial.

Although the only time I tried Casper (~ a year ago) I gave up on it because I couldn't get this stuff right anyway.

Anyway, my point was that there exist libraries in which "then" is a function and the object with this function isn't a promise. These libraries will end up in a terrible confusion when used with Futures.
You think you're resolving a future with an object and... oops! the built-in Future algorithm confused your object for a promise. Suddenly, not only are you not resolving your promise with the value, but your .then method is called unexpectedly.

Apologies, I wasn't very clear. I completely agree with this point and was just trying to reinforce it :)


In any case, considering that an object with a 'then' function is a promise is a recipe for confusion. Promise/A+ folks asked for and are happy about it. The rest of the webdevs who aren't aware of this subtle non-intuitive rule will have a very hard time when, for whatever reason, they have a 'then' function in a resolve value and their code really behaves in a way they don't understand.


I agree it's a recipe for confusion. But the weight of legacy code (growing by the day) may be too much to bear.
What about the weight of legacy non-promise code using "then"? The best thing we can say now is that we know nothing about it and it'd be wise to wait until more data on "then" is available. The Casper example should at least worry us. I hope it will be the browser vendors choice.

Hopefully not -- it's really very simple for Promises/A+ libs to add `then` to the Promise prototype.
Aren't they already doing that? I don't understand your point here.

No, DOMFutures ships with this OOTB. If there were an es Promise or Future builtin I suspect there would be a lot of pressure to specify it with `then` on the prototoype to make its instances compatible with existing Promises/A+ libs. That's the crushing weight of legacy I'm referring to.

What occurred to me is that it really is just a few lines of code for each of these Promises/A+ libs to add to tack on the prototype `then` without having to muddy the spec. In hindsight this seems obvious. I wonder why DOMFutures didn't go this route? It may not be too late.


I don't think in the entire platform there is a precedent of doing this (maybe for a good reason?). We'll see what web browsers end up implementing.


IMHO __proto__ is one precedent -- and we know how that's going :P
Once again, __proto__ is not a good comparison. It's already in the platform. As far as promises are concerned, the platform has exactly no obligation to follow the current Future or an alternative that'll emerge tomorrow; no obligation to follow Promise/A+ or whatever else.

You said "I don't think in the entire platform there is a precedent of doing this". I assume by "this" you meant using a forgeable string key that could lead to confusion. That is what we were discussing, right? If so I think __proto__ is a great example. And again, I think it helps make your (our) point :)


________________________________

es-discuss mailing list
es-discuss@mozilla.org<mailto:es-discuss@mozilla.org>
https://mail.mozilla.org/listinfo/es-discuss




--
-kg

Received on Wednesday, 24 April 2013 17:03:53 UTC