-
Notifications
You must be signed in to change notification settings - Fork 2k
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
async/await semantics (diamod arrows as demonstration) #3813
Conversation
This subfolder will be included into test-suite if generators will be detected at runtime. Do not using --harmony flag sniffing anymore.
This will be needed when language will support asynchronous features like async/await or similar.
My personal belief is that it is best for generator and async functions to be explicit, but @jashkenas seems to have leaned towards implicit generator syntax, so I try to stay consistent with that in #3757, by using |
@GabrielRatener no, they don't. The semantics of EcmaScript allows replacing the result of the constructor [if you are using explicit return of non-primitive type inside the constructor, then the result of the |
My bad, that does actually seem useful. |
@GabrielRatener I've considered asyncFn1 = ->
result = wait for myPromise # awaitng for the promise
results = all of [promise1, promise2] # shorhand for Promise.all()
winner = race of [promise1, promice2] # shorthand for Promise.race()
...
asyncFn2 = (a, b) ->
x = wait for someOtherAsync a
y = wait for someOtherAsync b
return x + y where |
"await" might be a bit too widely used (after a very quick github search, it seems moslty used as part of compound names, though..), yes; but I'm not sure we want to use another name. If we add it as a keyword, |
@Artazor The 'race' and 'all' examples are not backwards compatible because of the |
What about keeping |
@GabrielRatener yes, unfortunately |
Let's brainstorm! x = promise! # resolution of a single promise
y = promise!.promiseField!.promiseFunc(a,b)!.nonPromiseField # nice chaining And looks that it will not produce any ambiguities. Will it? |
@Artazor that might be confusing, since in ruby |
what about |
@bcherny I know about this mnemonic. I've supposed that being reminiscent of mutating modifier would be relevant for the resolution of promises in some sense. Why? Promise being resolved changes it's own state from @GabrielRatener, I appreciate your trial to keep diamonds in a language, but I'm afraid that A. if an B. which forms of the prefix operator — better for standalone calls without chaining. I don't like x = ~< (~< (~< a).b).c # ugly
x = ~< fn x, y, z # nice (except the ~< itself)
~< sleep 100 # nice postfix operator — better for chaining, but ugly for standalone calls: x = a!.b!.c! # nice
x = fn(x, y, z)! # ok
x = (fn a, y, z)! # strange
sleep(1000)! # ok
(sleep 1000)! # ugly both? x = a!.b!.c! # nice
x = (!) fn x, y, z # ok
(!) sleep 100 # ok or kinda inconsistent x = a!.b!.c! # nice
x = ~< fn x, y, z # nice but inconsistent
~< sleep 100 # the same Any other variants, thoughts? |
@Artazor Yes, finding a solution to backwards compatibility is not easy. I am starting to think that supporting bacwards compatibility of the |
+1 for postfix |
@Artazor I tried the IcedCoffeeScript implementation from maxtaco, which uses just the additional keywords Some ideas for the planned implementation you are talking about: E.g. when waiting for multiple async methods to be finished: await [
method1: ->
method2: ->
]
async
# code beeing executed after all methods have been finished E.g. to chain methods currentState = getIn() await order() await getBeer() await drink() async getOut() where currentState will have the result of getOut after all methods have been chained. currentState = await getIn() achain order() achain getBeer() achain drink() async getOut() This approach would propably keep the syntax clean to use cascaded structures like currentState = await getIn() achain (await getMenu() achain order() async pay()) achain getBeer() achain drink() async getOut() This could also be expanded to await
achain [
getIn()
order()
getBeer()
]
async
currentState = getOut() or as alternative notation: currentState = await
achain [
getIn()
order()
getBeer()
]
async
getOut() # which implicitly returns and its return value will be applied to currentState For a race promise currently i have no idea without an additional keyword. There might be some things i missed out, e.g. parameters for the async callback. I would really like to see a stable async feature in CoffeeScript, What do you think about this? |
Lets all remember that coffeescript broke backwards compatibility when introducing implicit generator syntax. Considering @jashkenas pushed for this approach over an explicit syntax as in JS, it seems backwards compatibility is a low priority for the devs. |
I do not think it broke backwards compatibility. There are only a few brackets, commas and no semicolons required when writing CS code. Developers using CS are interested in writing code with less effort, using just reachable keys without wrenching fingers to type special characters. So in scope of CS everything seems to be clear, there are no underscores in variable names, no dollar signs, nor questionmarks. So introducing new keywords like |
I think that Other thing that I've started to think of is the performance of the proposed semantics, since @jashkenas blamed me for being "not smart" in usage of the new JS features. The point within current discussion is the fact that code inside generator functions is not optimized at all (in v8). As well it is not optimized when the function contains the @jashkenas, what are you thinking of the generator-less awaits? They will work on the client-side as well without any 6to5 postprocessing... |
To clarify your discussion, generator support never broke backwards compatibility, "yield" was a reserved word and could not be used in the past. |
@alubbe, exactly! I think if |
since CS 1.3.0 Line 584 in de511e0
|
Guys, let the concrete syntax for the later stages of the discussion. What I want to discuss here too is the possibility to implement Here is an example how Examples are written with "throw early" policy adoption: if promisified code has a synchronous preamble that throws then we prefer to throw instead of creating rejected Promise. Note, that patterns below don't cover control flow breaks like Asynchronous sequencefunction() {
var b, e;
a();
b = await c;
d();
e = (await f[3].f()).x.y.z;
g();
return h;
} when desugared into Promises becomes function() {
var b, e;
a();
return c.then(function(_ref){
b = _ref;
d();
return f[3].f().then(function(_ref){
e = _ref.x.y.z;
g();
return h;
});
});
} Note, that we can reformat the code to make it line-to-line prefect: Asynchronous selectionfunction() {
var d, n;
a()
if (x < y) {
b();
d = await e();
f();
} else {
m();
n = await p();
q();
}
r();
return x;
} when desugared to promises becomes: function() {
var d, n;
a();
function _if() { if (x < y) {
b();
return e().then(function(_ref){
d = _ref;
f();
});
} else {
m()
return p().then(function(_ref){
n = _ref;
q();
});
}
return _if().then(function(){
r();
return x;
});
} Asynchronous repetitionfunction() {
var i, c;
a();
i = 0;
while (i < 10) {
b();
c = await d();
e();
i++;
}
f();
return g;
} when desugared to promises becomes // Repetition
function() {
var i, c;
a();
i = 0;
function _while() {
if(i < 10) {
b();
return d().then(function(_ref){
c = _ref;
e();
i++;
return _while();
});
} else {
return Promise.resolve();
}
}
return _while().then(function(){
f();
return g;
});
} @jashkenas, is it possible for CS to generate such structures? Or you don't like this? |
I would be happy to see async await land in cs. |
@benzen, yes, it is intentional, you should polyfill the Promise by yourself, as it is already landed in io.js and soon will be a standard feature. Also you can assign an alternative implementation into global Promise (e.g. Bluebird) |
-1 for postfix. Hard to skim for promises. await (await fetch(...)).json() Seems fine. |
@michaelficarra, async / await is moving right a long. Currently at Stage 3. Stage 4 ("Finished") by the end of November |
@drewhamlett thanks for the update! Can't wait 👍 |
Coffeescript should support await and compile to js code containing async/await just like it supports yield and compiles to generator*/yield. Then if you want to run it on an engine that does not support ES7 you can transpile the js code as a second stage. It would be strange if coffee compiler tried to polyfill new features. That being said in the mean time we can do something very similar to await with things that are already supported with little syntax overhead like this https://github.com/davibe/async-js-coffee/blob/master/src/4_generators_promises.litcoffee . Then once await will be supported its going to be simple to adapt things |
I would like to see a backwards compatible syntax introduced to CS < 2.0. Essentially an alternative to the ??? |
I like the idea of tapping into CoffeeScript's functional ancestry and going with a reflected "Monad sequencing operator" from Haskell.
Resulting in:
The nice touch here is that assignment to a variable is also a reflection of the Haskell monad sequencing with assignment (
|
We should start compiling async/await pretty soon - at the time of writing (December 2015), it has been implemented in one browser, Microsoft Edge, and I'm guessing we'll see implementations in more of the major javascript engines by the end of 2016. I don't think we need to do anything fancy, like compiling async and await to es6 code à la iced-coffee-script, or by automatically restructuring code to use promises as @Artazor proposed. While I like the idea, and was actually trying to figure out how to do that earlier today before I found this discussion, I think it belongs in a fork of coffeescript. Instead just compile straight to ES7's async function syntax. From a practical perspective, there's already tools out there, i believe, that will regenerate the compiled javascript code.
@drewhamlett thanks for the update and the link. Food for though: since coffeescript's |
about your third point: We already have "implicit generators" when the function contains |
I agree with @phaux
Using more symbols for different kinds of functions in CS when a keyword is used in JS will just get confusing for people to learn CS and ultimately make it harder to read. For generators this was different because the * is used in JS as well, for better or worse. Staying as close to JS as possible is really important to sell people on using CS for projects. I would only trade words for symbols if any bum off the street could look at the change and say "now that is super helpful and I can remember that". Otherwise we are going backwards. There is a reason |
+1 for any solution that allows nice async control flow with coffee. I would love to use coffee for all my tests insteaf of typescript but I need a clean way to await stuff for the tests to stay clean and easy to read. |
+1 for |
+1-ing in hopes that CS can somehow be resuscitated to implement new ES6/7 features. Increasingly prevalent use of async/await & decorators are starting to force my hand to make the switch, but I much prefer CS. ES6 does more to obfuscate than improve javascript, and its grammatical conventions remain far more tedious than Coffeescript. |
@AJeezy9 totally agree, was sad to move away from CoffeeScript. Personally I would love something like "babel-lite" where a huge amount of features are forcibly removed. The only thing that I always really miss from CS is the "one-page" docs (much less syntax than js) and the "everything is an expression" which works so well with JSX and functional programming. @Artazor regarding your "promise unwraping" proposal It has been partially implemented here (in babel) and works pretty well, i'd definitely +1 that. |
I don't necessarily agree on that. I'd prefer Coffee to stay one step ahead of JS and make things prettier and more concise.
Then how about using a different sigil? ( download fetch('/gallery.html')~^.text()~^.match(/<img src="(.*?)"/)[1] |
Concerning the existence of a postfix syntax in general, it's not that I'm against the idea but I don't feel the need for this. Though, I didn't find any of the suggested operators appealing. I personally feel Besides, to me all the pseudo-arrow usage result in ugly syntax. I would hate to write something like: I find way more elegant the idea of an operator such as I'm new to this discussion, and I'm a bit afraid by the time it's taking to add a feature that is said to be ready (forgive me if I missed a post saying the opposite, the thread got quite long now, so please be indulgent if it is the case). I love CoffeeScript for its elegant syntax like most people on this thread, but what I primarily like a language for is the features it provides. Wouldn't it be more logical to add the feature with the simple |
@mikeyhew said:
I disagree, where would the confusion arise? Implicit async definitely seems like the way to go - if it's a function, it's a function. If you see 'await' then you know that line of code is 'awaiting'... It seems like the only elegant solution proposed. |
@DomVinyard having both implicit generator functions with Even Python 3.5 went with explicit syntax for async functions. |
@DomVinyard I've mostly changed my mind since I made that comment. At this point, I don't think it would be the end of the world if a function returns a promise because it contains an |
@GabrielRatener No mixing |
@GabrielRatener said:
yes, python 3.5 uses |
@mikeyhew And it would reduce the friction for people just starting out with async, they can throw an await call into an existing production function without the mental overhead of what some would consider "refactoring the whole function". @jashkenas Count me +1 |
@mikeyhew The reasons for adding explicit syntax in Python are described here. I think both explicit and implicit syntax would be better than nothing, but explicit syntax really indicates that the functions are fundamentally different. @DomVinyard Async generators will be in JS eventually, so shouldn't accommodations be made for the future syntax? Also, implicit syntax can cause friction and confusion for people who understand that JS is single threaded. They see rogue |
FWIW, @zapu and I recently rewrote IcedCoffeeScript to use ES6 generators. The compiler and transpiled code are significantly simpler, but the semantics remain the same. Code is available in our iced3 branch or via npm at iced-coffee-script-3. |
I totally agree, I'm making my case based on the idea that coffeescript is appealing to the very people who prefer the magic over the explicit, fans of abstraction. Although I do take your point that it would be alienating to some, I think it's absolutely worth the conceit. In the same way that - if an explicit form won out - I think there are a group of people that would use the async function form by default 'just in case'. Like many do with the fat arrow. |
Such cases are easily handled with a linter. I don't see this point as a valid reason. I have a preference for the implicit syntax (without the async keyword). As many said, it keeps the logic of implicit generators. However I have no idea if it could be a problem to later bring async generators to coffeescript as some were discussing. |
The link to the Python discussion was interesting, thanks for posting up
Am I right in saying that this limitation doesn't apply to ES7 because
|
Closed in favor of #3757. |
I'm aware of #3757 but I don't think that we should speak in terms of the future ES7, let stay in the pragmatic domain. This PR is intended to start a discussion and research in this field.
I've discovered that await semantics can be achieved by a tiny utility, much more simple than that of #3757
Here it is:
Also small tweaks in compiler enable async'ed functions to be used as inheritable methods of CS classes, and even more - as constructors (in both cases
super
is available), so the inheritance and extends will not cause any issues (like they do for ordinary generators when used withco
).I've chosen a bit ridiculous syntax for async'ed functions:
-<>
and=<>
and keptyield
for resolving promises (instead ofawait
). This is not critical and easily can be refactored intoawait
andawait return
style as of @GabrielRatener (and keep->
and=>
).You can review the test (/test/generators/async).
Also I've moved all generator-related test stuff into separate directory, and removed --harmony flag sniffing (is my way of detecting generators valid enough?) - this is separate commit.
Also in the separate commit I've extended testing framework to be capable for dealing with promises.
The main question is: is the proposed semantics useful enough to be included into language in some way?
I think that generators+promises coding patterns will be very popular at the server side (especially with io.js)
and there are so tiny conservative changes needed from the language to enable brand new type of libraries/packages to emerge.
@GabrielRatener, @jashkenas, @alubbe, @michaelficarra and other guys - what are you thinking about it?
P.S. All discussed things are already implemented in this PR