GithubHelp home page GithubHelp logo

tc39 / proposal-async-await Goto Github PK

View Code? Open in Web Editor NEW
1.6K 112.0 103.0 315 KB

Async/await for ECMAScript

Home Page: https://tc39.es/proposal-async-await/

License: Apache License 2.0

JavaScript 4.90% HTML 95.10%

proposal-async-await's Introduction

Async Functions for ECMAScript

The introduction of Promises and Generators in ECMAScript presents an opportunity to dramatically improve the language-level model for writing asynchronous code in ECMAScript. The spec text can be found here.

This proposal is implemented in a regenerator which can compile ES5 code containing async and await down to vanilla ES5 to run in existing browsers and runtimes.

This repo contains a complete example using a large number of the features of the proposal. To run this example:

npm install
regenerator -r server.asyncawait.js | node

proposal-async-await's People

Contributors

amanda-mitchell avatar anba avatar arv avatar benjamn avatar bterlson avatar domenic avatar eventualbuddha avatar forivall avatar greetclock avatar jayphelps avatar ljharb avatar lukehoban avatar michaelficarra avatar olalonde avatar ratson avatar rictic avatar rwaldron avatar vjeux avatar ysangkok avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

proposal-async-await's Issues

Await and Thenable Coersion

As currently specified, the await operand is converted to a Promise and then unwrapped via then. If the operand evaluates to an object containing a function-valued property named then, the recursive unwrapping machinery of Promise.prototype.then will attempt to unwrap that object, possibly leading to surprising behavior.

This is especially problematic for async functions, because with async functions the underlying Promises become more transparent to the user, and hence this coercion behavior becomes more surprising.

I imagine that this issue could arise for users attempting to create a DSL where logic is encoded using methods with names like "then" (to give one example).

So... is this a problem?

If so, then I think there might be some clever ways to work around it. Perhaps, within the async pump, we could test for the presence of a property named with a built-in symbol like Symbol.isPromise. This strategy is already used within the spec in other places.

Why is this in terms of generator objects?

It's often cited that there's an isomorphism between async functions and generator functions. That makes sense, and I'd expect isomorphic control flow structures to be used.

However, this spec seems to directly use generator objects, %GeneratorPrototype%, methods named next and throw, and more. In fact, it seems to be transliterating the appendix's desugaring into ECMAspeak, from what I can tell.

This isn't really OK. Async functions and generators need to be separate, so that they can evolve divergently as necessary. There must be no dependency upon generators in the definition of async functions, but instead they should be their own state machine that does not reuse generator objects. This allows future extensions to be much more flexible, and implementations to be written in a more optimized fashion, by not exposing any internal machinery.

Awaiting Non-Promises

Should awaiting non-promises result in scheduling another task on the microtask queue? Consider this implementation of an async mutex:

https://github.com/zenparsing/ziptar/blob/master/src/Mutex.js#L33

An implementation would use such a construct like so:

await myMutex.lock($=> {
  // only one at a time in here
});

Now if no-one is currently inside the mutex, do we really need to make the caller wait until the current task queue is flushed before allowing it to enter the section?

Fix errors in default parameters

TC39 wants to see errors thrown from default expressions reject the returned promise rather than throw out. This will likely be an undertaking and require significant refactorings to other areas of the spec. See also #35.

Carefully scrutinize function kind spec stuff. Do we need a new one?

There are apparently three notions of function kind in the spec. FunctionKindA is normal/method/arrow; FunctionKindB is normal/classConstructor/generator; FunctionKindC is normal/non-constructor/generator.
#50 and #54 update the spec to pass "non-constructor" to FunctionAllocate (FunctionKindC), which causes [[FunctionKind]] (FunctionKindB) to be "normal".

In contrast, generators have their own FunctionKindB and FunctionKindC, "generator", which is treated specially throughout the spec. We need to do an audit of all those special treatments and see if async functions need any similar exceptions, and if so, create new FunctionKindB and/or FunctionKindC.

Cover Grammar

It looks like currently there's no way for the async ident => form to be parsed by AsyncArrowFunction, since CoverCallExpressionAndAsyncArrowHead requires the parens in the argument list.

Also, I see that we have a no-newline restriction at the end of a production, which feels a little strange. I think I've changed my mind about removing the no-newline restriction before the => in the BindingIdentifier case on the grounds of consistency. If we add that restriction back in, I think this might be what we're after?

AsyncArrowFunction[In, Yield, Await]:
    AsyncArrowHead [no LineTerminator here] => ConciseBody[?In, Await]

AsyncArrowHead[Yield, Await]:
    async [no LineTerminator here] BindingIdentifier[?Yield, Await]
    CoverCallExpressionAndAsyncArrowFormalParameterHead[?Yield, ?Await]

CoverCallExpressionAndAsyncArrowFormalParameterHead[Yield, Await]:
    MemberExpression[?Yield, ?Await] Arguments[?Yield, ?Await]

Supplemental:

AsyncArrowFormalParameterHead[Yield]:
    async [no LineTerminator here] ArrowFormalParameters[?Yield, Await]

Could totally be wrong though; cover grammars are a pain. (Hopefully this will be the last one for a while...)

Maybe @allenwb might want to give this a look?

Returning a non-awaited Promise from an async function

I'm not sure if this issue has been addressed but could possibly be an area of confusion.
In the analogous C# Task its possible to decouple the parent 'Task' from the child 'Task'
An example can be seen here (msdn). It would seem as it is defined currently, the following would not be possible with async functions:

async function doSomethingAsync () : Promise<string> { ... }

Though the behavior is to be expected when returning a promise from within a promise. There may be cases where that may not be desired.

-- This behavior may only be relevant to future promise specification.

await* and parallelism is not ideal

The proposed await* syntax is not ideal for parallelism. When managing parallel tasks, it is typical to have a set of dissimilar tasks, each launched and finished in a different way. Instead of reducing boilerplate, an idiom where each result needs to be packed and unpacked into an array would increase ceremony

Can we do something that is closer to what is achieved by Iced CoffeeScript? There they have a keyword which introduces a block, within which you should not expect to be able to use the result. Their syntax is something like this:

await
  $.get myurl1, defer result1
  douserinput defer result2
  for j in [0...10]
    $.get myurlarray[j], defer resultarray[j]
# within the wait block, all the requests are launched in parallel...
# then the await block ends, which pauses the program, then after that we have results
answer = result1+ result2 + sum(resultarray)

The main advantage here is clarity: the variable name for the result is directly next to the operation that computes it.

Rather than an array-based await* I think it would be much better to try to mimic Iced's simplicity:

await* {
  result1 = await urlget(url1)
  result2 = await douserinput()
  for (var j = 0; j < 10; ++j) {
    resultarray[j] = await urlget(urlarray[j])
  }
}

Would something like that be possible?

Inheritance relationship between AsyncFunction and GeneratorFunction?

There have been multiple proposals to define semantics for async generator functions, the most promising of which is that they should return AsyncIterator objects: https://github.com/zenparsing/async-iteration/

Generator function objects are already instanceof GeneratorFunction, and it seems like this draft specification is proposing that async functions should similarly be instanceof AsyncFunction: http://tc39.github.io/ecmascript-asyncawait/#async-function-objects

I worry that introducing this AsyncFunction constructor is hostile to the future of async generator functions, since a single function object cannot be both instanceof GeneratorFunction and instanceof AsyncFunction, unless one of AsyncFunction or GeneratorFunction is in the inheritance chain of the other, since prototypal inheritance in JavaScript does not allow multiple inheritance.

Furthermore, I thought there was a general agreement on the wisdom of not making it easy to distinguish between async functions and functions that just happen to return a Promise, so I'm wary of introducing a constructor like AsyncFunction that invites instanceof checks.

Now, since an async generator function returns neither a Generator object nor a Promise, but something different (an AsyncIterator), I could imagine that async generator functions would be neither instanceof AsyncIterator nor instanceof GeneratorFunction. Perhaps a new constructor would need to be introduced, called something like AsyncGeneratorFunction?

Suggestion: `await`ing functions

await waits for a Promise and returns the result. So awaiting something that is not a Promise should produce error. For example: var a = await 12 doesn't make much sense. I think awaiting a plain function or arrow function is the same, unless the function is treated as a Promise. That is:

async function w() {
    await resolve => setTimeout(resolve, 0);
}

IMO should behave the same as

function w() {
    return new Promise(resolve => setTimeout(resolve, 0));
}

this will allow to completely abstract use of Promise through async/await. If you want to actually return a function, you would use the return keyword instead.

Top level awaits

Is there any notion or plans for such thing? In Node we use doSomeStuffSync in global (module) space, where concurrency isn't required and order of commands really matters.

// this is global space
let x = a.doStuffSync();
let y = b.doOtherStuffSync(x);  

But global-space awaits could obsolete those Sync -suffixed functions and open the possibility to remove them entirely.

// this is global space, no "async" function wrapper
let x = await a.doStuff(); 
let y = await a.doOtherStuff(x); 

Of course, if a.doOtherStuff accepts promises it may be implemented on usual async versions, but if it's not (most of today's API), we'll have an option. I'm not sure about technical possibility for this, just asking. But this have special benefits like

let x = await a.doStuff(); 
console.log(x);
// vs
let x = a.doStuff().then(function(v) { console.log(v); return v; }); 

Can you new an async function?

It might be nice to allow new on an async function and have it do something useful...

async function Person() {
  this.name = await getName();
}

// seems weird-ish.
var p = new Person();
p.then(person => console.log(person.name));


// but seems better in another async function
async function CreatePerson() {
  var person = await new Person();
  // ...
  return person;
}

// also allowing new argues for an additional async form:
class Foo {
    async constructor() { ... }
}

The behavior of generators is to allow new (IOW, all other function forms can be newed) which argues for allowing it. However, generators don't allow this in their body. This would be a needless restriction for async functions so I'm not sure it applies.

If anyone is aware of code today where new is expected to return a promise for an instance it would be a strong argument in favor of allowing new.

I think the only viable alternative is to make newing an async function an error.

clarifying the `await*` example's processing model

In the main README, a suggestion is made about a possible future (beyond this spec) await* syntax, and this is the example given:

async function getData() {
  var items = await fetchAsync('http://example.com/users');
  return await* items.map(async(item) => {
    return {
      title: item.title, 
      img: (await fetchAsync(item.userDataUrl)).img
    }
  }
}

The example specifically shows the use of map(..), which leads me to assume that this is the mechanism by which all the callback-invocation promises are "transported" back to the hidden Promise.all in await*. That is, it implies that we have an array of promises returned back from map(), and that's how await* can consume them.

But what about the other iterators, which don't do this mapping, and don't even necessarily return an array themselves (though they all operate on arrays), like: forEach(..), some(..), filter(..), reduce(..), etc? Is there anyway await* could be used with them?

It would require a deeper, special integration between await* and these iterators, where the internal list of those promises from each iteration invocation wouldn't be "discarded" (due to the semantics of each iterator), but could somehow be alternatively tracked internally in the iterator, and then "transported" back to the await*. For everything but map(..), this would have to be a different mechanism than just the simple return value array that map(..) gives.

To put it another way, instead of the processing model being:

  1. run the map(..) iteration on the right-hand side, then
  2. take the array of promises and pump it into await*

...the processing model would/could be a little more like new fn(..), where the new operator changes how the fn(..) invocation occurs:

  1. await* could/would change how the right-hand side is processed, such that the special behavior I'm suggesting would be possible.

Is this just reaching too far? I can think of several cases where, for instance, it would be nice to be able to use await* on filter(..)'d arrays, to replace various libraries that provide async versions of these various iterators for you.

async getters discussed?

Curious if there's been any discussion about async getters?

AsyncMethod :
    get async PropertyName (StrictFormalParameters)  { FunctionBody }

I can see arguments both ways, but the spec currently allows you to await regular properties, so why not properties that are computed via a getter?

Just looking to start an open dialog if one hasn't already.

Some examples for reference...

Assuming this setup:

function fakeAjax() {
  return new Promise(resolve => {
    setTimeout(() => {
      resolve([new Comment(1, 'First comment')]);
    }, 2000);
  }); 
}

class Post {
  constructor(id, body) {
    this.id = id;
    this.body = body;
  }
}

class Comment extends Post {}

Currently supported by traceur:

class Article extends Post {
  constructor(id, body) {
    super(id, body);
    this._comments = null;
  }

  async fetchComments() {
    if (this._comments === null) {
      this._comments = await fakeAjax();
      this._comments[0].body += ' of the day';
    }

    return this._comments;
  }
}

async function main() {
  let article = new Article(123, 'Hello World');

  // Comments may or may not be loaded yet
  let comments = await article.fetchComments();

  console.assert(comments[0].body === 'First comment of the day');
}

Not currently supported by traceur or spec:

class Article extends Post {
  constructor(id, body) {
    super(id, body);
    this._comments = null;
  }

  get async comments() {
    if (this._comments === null) {
      this._comments = await fakeAjax();
      this._comments[0].body += ' of the day';
    }

    return this._comments;
  }
}

async function main() {
  let article = new Article(123, 'Hello World');

  // Comments may or may not be loaded yet
  let comments = await article.comments;

  console.assert(comments[0].body === 'First comment of the day');
}

But you can work around this spec omission by doing this:

class Article extends Post {
  constructor(id, body) {
    super(id, body);
    this._comments = null;
  }

  async _fetchComments() {
    this._comments = await fakeAjax();
    this._comments[0].body += ' of the day';
  }

  get comments() {
    if (this._comments === null) {
      this._fetchComments();
    }

    return this._comments;
  }
}

async function main() {
  let article = new Article(123, 'Hello World');

  // Comments may or may not be loaded yet
  let comments = await article.comments;

  console.assert(comments[0].body === 'First comment of the day');
}

Investigate usage of modified intrinsics with `await`

Per discussion:

  • Promise = class MyPromise extends Promise {} ; async function foo() {} - is the Promise returned by foo() a MyPromise? Or is it the original, now mostly-otherwise-inaccessible Promise?
  • await foo; Under what circumstances does this call .then on the object, thus Promise#then (which may be modified)? If I modify Promise.prototype.then, should that be called when await foo, or not? What if I delete the constructor property on foo?

The use cases I'm thinking of are:

  1. I should be able to modify the global Promise in such a way as its impossible for users to ever retrieve the original (I do this now with RegExp and Number, for example, in the es6-shim), and await and async functions should work with the modified global.
  2. I think I should be able to modify Promise.prototype.then such that await foo will always invoke my replacement function.

Editorial and styling nits

Bind generator to callers 'this' to enable async methods

Unless I've missed something, the construct:

    function aClass() { this.value = 0; } 
    function aClass.prototype.inc = async function() { 
        this.value += 1 ;
        return this.value ;
    }

    var x = new aClass() ;
    var y = x.inc() ;

...fails because within the async function 'this' is not bound to x. In my own implementation, this is fixed by modifying the 'spawn' function so that

function spawn(genF) {
    return new Promise(function(resolve, reject) {
        var gen = genF();
        ....

is replaced by

function spawn(genF,self) {
    return new Promise(function(resolve, reject) {
        var gen = genF.call(self);
        ....

...and the invokation rewrite from

  async function <name>?<argumentlist><body>
  =>
  function <name>?<argumentlist>{ return spawn(function*() <body>); }

to

  async function <name>?<argumentlist><body>
  =>
  function <name>?<argumentlist>{ return spawn(function*() <body>,this); }

Performance, Await, and Non-Promises

Revisiting an old concept just to be sure...

It will be beneficial in many situations to write functions that deal gracefully with input that can be either a promise or a non-promise:

async function processDeps(depList) {
    // depList can be an array or a promise for an array
    depList = await depList;
    // etc...
}

In the above example, a microtask will be queued for depList, even when depList is not a promise.

What can we say about the performance implications of this pattern, given that the current setup feeds all awaits through the microtask queue?

The alternative would be to have await check whether depList is a promise (in the same manner that Promise.resolve does), and immediately continue if not.

Clarifying question: can await be used in top-level code?

In the proposal, it looks like "await" is to be used inside async functions.

Can "await" be used in top-level code?

The reason is, when teaching students, it makes a lot of sense to start with top-level code that does not contain function definitions, and it also makes sense to "await input", such as in this example using Iced CoffeeScript's "await". What would this example look like in this proposal?

http://pencilcode.net/edit/guessmynumber

Async arrow syntax

Several folks have raised concerns about the async arrows syntax in this proposal. In particular:

http.createServer(async (req,res) => { 
  await delay(1000); 
  res.end('Hello World\n') 
});

There are generally two issues:

  1. Can this deal with ASI such that there are not ambiguities and such that this is forward compatible with the option of leaving off the argument list entirely?
  2. Is this sufficiently readable as a syntax?

For the first, the concern is something like:

async ()
  => 3

Under a future proposal that allowed eliding arrow parameter lists, this would have to parse as two statements. So we would need to ensure it was not legal in the current proposal. I believe that can be managed by adding a [NoLineTerminator] between the parameter list and the => of the async arrow grammar. This would additionally mean that async arrows themselves could not elide their parameter lists, as this would be interpreted as a single parameter arrow in ES6 already.

For the 2nd, I personally believe this is syntactically okay. It is challenging to parse since it is not a reserved keyword, but it is possible to do using a cover grammar, and is something implementations can reasonably do. For users, this is very similar to "function", and when colorized in editors as a keyword or contextual keyword will be similarly easy to read.

Opening as issue to track feedback.

Scheduling of an async function

Given the following piece of code:

var list = [];

async function f() {
  list.push(1);
}

var promise = f();
list.push(2);
promise.then(() => {
  /* CODE */
});

What shall be the value of list at the beginning of CODE? Given the implementation of spawn, it will be [1, 2]. On the other hand, the usage of async functions in https://github.com/jhusain/asyncgenerator seems to indicate that the output shall be [2, 1], which in fact may make more sense.

Proposal: Adapt return value to different Promise implementations

It would be a good thing if async functions would return the same type of promise that was awaited on in their function bodies. This would align with how typescript is implementing it (according to this issue). The async/await keywords could then be used together with various promise implementations without loosing the bells and whistles that comes with various Promise libraries such as bluebird, Q, WinJS and Dexie.

External Promise implementations can have features that would be lost if converting them to the 'standard' Promise as suggested in the readme of this repository.

Therefore, I would propose a more adaptive implemetation of spawn() that can accept and forward any A+ compatible Promise that was awaited for:

function spawn(genF, self) {
    var gen = genF.call(self);
    function step(nextF,initial) {
        var next = nextF();
        if (next.done) {
            // finished with success, resolve the promise
            return initial ?

                // Nothing was awaited. Direct return value must be converted
                // to promise:
                Promise.resolve(next.value) :

                // Return the value as is and let the promise
                // implementation take care of the A+ compatible promise chain
                next.value;
        }
        // not finished, chain off the yielded promise and `step` again
        if (!next.value || typeof next.value.then !== 'function')
            return step(function(){
                // Don't accept awaiting a non-promise such as "await 3;".
                // By not accepting that, we could detect bugs better.
                return gen.throw(new TypeError("Only acceptable to await a Thenable"));
            }, initial);
        return next.value.then(function (v) {
            return step(function () { return gen.next(v); });
        }, function (e) {
            return step(function () { return gen.throw(e); });
        });
    }

    try {
        return step(function() { return gen.next(undefined); }, true);
    } catch (e) {
        // Failure before code has yielded any Promise.
        return Promise.reject(e);
    }
}

Specifically, for my library, Dexie.js, there is a feature that enables 'Promise-Specific Data' which is analogue to 'thread static data' for promises. The entire Dexie API depends on that feature because it enables things like reentrant mutexes, implicit transaction scope, sub transaction awareness and some more API sugar. I believe that bluebird, Q, WinJS and others also would benefit from having this openess in how async/await behave.

By not binding the async keyword to a specific implementation of Promise, we would allow new Promise implementations with features that we cannot think of today and still use them with async/await.

Possible to await non-promise synchronously?

Is this legal?

await 1;

Furthermore can the expression above evaluate synchronously?

The answers to these questions impact the asynchronous generator proposal. Ideally the answers would be "yes" and "yes" respectively. The reasoning is that we would like to be able to produce observables that respect back pressure like so...

for(x on xs) {
await yield x + 5;
}

...without paying any performance cost in the event that the generator sink can process items synchronously (ie. next() does not return IterationResult for each item it receives).

Developers would be more likely to create event streams that respect back pressure if they did not have to commit to paying a performance cost regardless of whether the sink was synchronous. Note that when asynchronous I/O operations are performed, most of the time data is being inserted directly into a buffer synchronously. Forcing every item to be inserted into the buffer on a separate turn would be very expensive.

Grammar for interfacing existing callback code with async/await

Whilst Promises provide an mechanism to relate the (considerable) body of callback-based code with async functions, the syntax is relatively verbose and implementation dependent (i.e. it requires and relies on knowledge of the spawn() function) potentially restricting the possible set of implementations.

This issue suggests a mechanism to minimize the boilerplate needed to interface existing code with async/await by allowing callbacks contained within async functions to resolve and reject their container async functions, and prevent the container resolving automatically on exit without a return or throw.

Consider the code below, which has only 1 functional line (2 including the declaration):

function sleep(t) {
    return new Promise(function(resolve,reject) {
        setTimeout(resolve,t) ;
    }) ;
}

An alternative would be to pass the resolve/reject parameters to the re-written generator with two additional parameters (in this example asyncReturn and asyncThrow), parsing

async function sleep(t) {
    setTimeout(asyncReturn,t) ;
}

to

function sleep(t) {
    return spawn(function*(asyncReturn,asyncError){
        setTimeout(asyncReturn,t) ;
    }) ;
}

The same translation works for direct calls as well:

async function get(url) {
    var x = new XMLHttpRequest() ;
    x.onload = function(){
        if (x.status===200)
            return asyncReturn(x.responseText) ;
        else
            return asyncError(new Error(x.responseText)) ;
    } ;
   x.open(url) ;
   x.send() ;
}

...translates to

function get(url) {
    return spawn(function*(async,asyncError){
      var x = new XMLHttpRequest() ;
      x.onload = function(){
          if (x.status===200)
              return asyncReturn(x.responseText) ;
          else
              return asyncError(new Error(x.responseText)) ;
      } ;
     x.open(url) ;
     x.send() ;
   })
}

This can be easily implemented by:

  1. Passing the Promise resolve and reject routines in spawn():
function spawn(genF,self) {
    return new Promise(function(resolve, reject) {
        var gen = genF.call(self,resolve,reject);
        ....
  1. Allowing async functions to NOT automatically resolve on return, but providing a method to indicate resolution will take place in the future. I have implemented this by disallowing the resolved value to be the resolver argument (since this is pointless), and always appending a synchronous return to the wrapped generator body.

The rewrite rule now becomes:

async function <name>?<argumentlist>{<body>}
=>
function <name>?<argumentlist>{ return spawn(
    function*(asyncReturn,asyncError) {<body> ; return asyncReturn ; },
this); }

...and spawn is updated to:

function spawn(genF,self) {
    return new Promise(function(resolve, reject) {
        var gen = genF.call(self,resolve,reject);
        function step(nextF) {
            var next;
            try {
                next = nextF();
            } catch(e) {
                // finished with failure, reject the promise
                reject(e); 
                return;
            }
            if(next.done) {
                // finished with success, resolve the promise
                // ...unless the response indicates resolution by callback
                if (next.value!==resolve)
                    resolve(next.value);
                return;
            } 
            // not finished, chain off the yielded promise and `step` again
            Promise.cast(next.value).then(function(v) {
                step(function() { return gen.next(v); });      
            }, function(e) {
                step(function() { return gen.throw(e); });
            });
        }
        step(function() { return gen.next(undefined); });
    });
}

Syntax:
The use of the identifier asyncReturn (and asyncError) is possibly not wise. Additional rewrites would be required, but preferential syntaxes might be:

return async <expr>     =>     return <arg1>(<expr>)
throw async <expr>      =>     return <arg2>(<expr>)

await arrays and objects

with co, you can yield an array or object of "yieldables". with many promises, you can "join" arrays and objects into a single promise that returns the respective results.

in my opinion, these mechanisms should be built in, otherwise it would be useless. i would hate to do await Promise.all([promises...]). I want to be able to do await [promises...]. right now, the proposal seems unsure about it, but for me, this is a requirement for me to ever use this.

also, it should work recursively, so i should be able to do await [[[[promises...]]]].

it should also either throw or (preferably) ignore non-"awaitables"

await does not chain well - alternate syntax needed

await does not chain well. Consider:

result = await (await (await f1()).f2()).f3();

I've been using an async/await substitute (steamline.js) for several years and we have a lot of code that chains async calls. With streamline we write the above as:

result = f1(_).f2(_).f3(_);

The streamline.js syntax is a hack (for practical reasons it was easier to reserve a token than to extend the grammar) but why not use the sugar proposed in the concurrency strawman?(http://wiki.ecmascript.org/doku.php?id=strawman:concurrency):

result = f1!().f2!().f3!();

Parallel awaits

Currently it seems like the only way to use await with parallel functions is with Promise.all, as in https://gist.github.com/ajcrites/e6fd928f1a300bf40cea

It would make sense to me to implicitly convert some structure into a promise awaiting parallel promises to simplify the syntax somewhat. Parallel operations are common.

My suggestions would be:

await [promise1, promise2]
await promise1, promise2
[await promise1, await promise2]

I think that the first one makes the most sense -- semantically there should be no reason to call await with an array literal.

How do I await on an async function returned by await?

I've possibly (probably?) misunderstood either the precedence issues or contexts in which await is a keyword rather than a possible identifier, but clarification would be good.

This is from an issue at acornjs/acorn#309

// Return an async function asynchronously
async function x(n) {
    return async function xxx(m) { return n+m }
}

async function test() {
    var r = await x("a") ;
    console.log(r) ;        // [Function: xxx] - correct
    console.log(await r("b")) ; // "ab" - correct
    console.log(await (await x("c"))("d")) ;    // [ReferenceError: await is not defined] - fail! await() looks like a call
    console.log(await await x("e")("f")) ;  // Fail! x("e")("f") binds to second await and fails (not a Promise)
}

test().then(console.log.bind(console),console.error.bind(console)) ;

Loosen no LineTerminator here

async x => x

If the ArrowParameters is a single BindingIdentifier there is no need for the [no LineTerminator here] after the BindingIdentifier.

Question: OK to return Promises from async functions?

Reading the proposed implementation it looks like this should work because async functions support promise chaining in their return values:

async function exampleAsync() {
  return helperAsync();
}
async function helperAsync() { ... }

Specifically note that there is no await keyword in front of the call to helperAsync(). This actually seems good because it avoids an extra traversal up and down the stack compared to return yield helperAsync(). Just want to make sure this is intentional.

Proposal: Awaiting on Arguments

I was playing around with the current experimental implementation of async/await in Babel and thought it might be cool to be able to await on arguments. The most basic example:

async function double(await num) {
  return num * 2;
}

This is the same as the following ES6:

function double(num) {
  return num.then(x => x * 2);
}

Or the following ES5:

function double(num) {
  return num.then(function (x) {
    return x * 2;
  });
}

As another example, consider awaiting on multiple arguments:

async function multiply(await first, await second) {
  return first * second;
}

As far as I'm aware, this is the best analog in the current proposal:

Better analog (as pointed out in the comments):

async function multiply(first, second) {
  [first, second] = [await first, await second];
  return first * second;
}

And as far as I'm aware, this is the best analog in ES6:

function multiply(first, second) {
  return Promise.all([first, second]).then(([a, b]) => a * b);
}

Extending this even further, we can use the new ES6 arguments functionality with await:

async function multiply(await {num: first}, await {num: second}) {
  return first * second;
}

Which is the same as:

function multiply(first, second) {
  return Promise.all([first, second]).then(([{num: first}, {num: second}]) => first * second);
}

Additional example for awaiting on a property of an argument:

async function double({await num}) {
  return num * 2;
}

Which is the same as:

function double(obj) {
  return obj.then(({num}) => num * 2);
}

wishlist: some way of adding async/await to TypeScript (preprocessor, fork of compiler, sweet.js-like thing, whatever)

I know it's on the TypeScript team's roadmap, but it's so painful that you can get async/await and all of ES6 with traceur --experimental, but there's no way (AFAICT) to leverage that while still using TypeScript.

Most of the ES6 features I can wait for TypeScript to add, but async/await is both the biggest draw (IMHO) and the one that appears the furthest out on their roadmap.

Since it looks like the compiler transform and runtime impl are pretty well known at this point, is there any way to get async/await into TypeScript? I understand that tooling support will be extremely unlikely, but even if it's only supported by the command-line compiler, that would still be a huge win over having to keep waiting for TypeScript to get it.

Thanks!

Integration with synchronous inspection

I am floating this idea; not sure if I like it. It's for performance, but I am not sure of the consequences otherwise. I am actually kind of hoping someone can decisively tell me why it's bad.

Symbol.promiseInspect = Symbol("Symbol.promiseInspect");
Promise.prototype[Symbol.promiseInspect] = () => {
  // return one of: { state: "pending" },
  // { state: "fulfilled", value: v }, or { state: "rejected", reason: r }
};

// then:

var x = await foo;

// becomes approximately

const promise = Promise.resolve(foo);
const inspector = promise[Symbol.promiseInspect];
var x;
if (typeof inspector === "function") {
  var result = inspector.call(promise);
  if (result.state === "fulfilled") {
    x = result.value;
  } else if (result.state === "rejected") {
    throw result.reason;
  } else {
    x = yield promise;
  }
} else {
  // only happens if someone tampered with Promise.prototype
  x = yield promise;
}

The transformation for things not of the form x = await y is of course more complicated, but I guess I'm just really trying to illustrate the idea that you could use a synchronous inspection interface to potentially speed up these sorts of function calls.

Is this even observable, actually? Could this be an implementation-level speedup, without any Symbol.promiseInspect necessary? That sounds possible.

Sorting arrays with async functions

I'm wondering if it is possible to use an async function to sort arrays. I played a bit around in the babeljs REPL: see here

(async function() {
  function task(val) {
    function pReturn(a) {
      return new Promise((resolve, reject) => {
        setTimeout(() => {
          resolve(a);
        }, 500);
      });
    }

    async function getMe() {
      return (await pReturn(val));
    }

    return Object.freeze({
      val,
      getMe
    });
  }

  let tasks = [task(22), task(10), task(15)];

  console.log(JSON.stringify(tasks));

  tasks.sort(async function(t1, t2) {
    return (await t1.getMe()) - (await t2.getMe());
  });

  console.log(JSON.stringify(tasks))
}())

Is it not supposed to work or am I doing it wrong?

Awaiting within an arrow function

Assuming getJSON returns a promise for some JSON, does this work:

async function renderChapters(urls) {
  urls.map(getJSON).forEach(j => addToPage((await j).html));
}

Does the await within the arrow function halt the execution of both the forEach and renderChapters, or does it error because the arrow function is not an async function?

If not, should we have a way to await to the parent async function?

Question: Async/await vs decorators

I wonder if the proposal might be defined in a more generic way using some kind of a decorator function syntax.

E.g.

@async function* chainAnimationsAsync(elem, animations) {
    var ret = null;
    try {
        for(var anim of animations) {
            ret = yield anim(elem);
        }
    } catch(e) { /* ignore and keep going */ }
    return ret;
}

where @async is decorator function (basically spawn function from non-async example).

How to cancel a running async function

Is it possible to short-circuit a running async function like one can do with a generator by calling its return method?

A use case would be to have a finally block invoked in order to clean up resources, etc.

async_generators and for...of

let's take a look at the following snippet:

function* counter(){
  let i=0;
  while(1){
    yield i++;
  }
}

function wait(time){
  return new Promise(done=>{
    setTimeout(done,time);
  });
}

async function* delay(){
  for(const x of this){
    await wait(1000);
    yield x; 
  }
}

async function logAsyncCount1(){
  var it = counter()::delay();
  console.log('start');
  while(1){
    var obj=await it.next();
    if(obj.done) return;
    console.log(obj.value);
  }
  console.log('end');
}

calling logAsyncCount1 will output a number every second.

Wouldn't it be nice if logAsyncCount1 could be simplified using for...of ?

async function logAsyncCount2(){
 console.log('start');
  for(var y of /*await*/ counter()::delay() ){
    console.log( /*await*/ y); 
  }
  console.log('end');
}

Unfortunaly the proposal does not mention the usage of async-generators in the context of for...of
Furthermore, using babel, placing await in both places does not generate the expected output like logAsyncCount1 does.

i'd love to see this features play nicely together. This would allow us model dataflow in a very pleasing way:

example:

dataGenerator()
  ::delay(1000)
  ::computeStuff()
  ::tee((computed)=>{
    computed::toStore();
    computed::draw();
})

Syntax is underspecified

Is AwaitExpression a UnaryExpression? I think it is pretty clear that we do not want this to be an AssignmentExpression (like YieldExpression).

Allow class methods be async

I'm expecting something like this would allow class prototype methods to be async, but this is reported as a syntax error.

export default class {
  constructor () {
  }
  foo () {
  }
  bar async () {
    return await this.foo();
  }
}

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.