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

CS2 Discussion: Features: async/await #4908

Closed
coffeescriptbot opened this issue Feb 19, 2018 · 32 comments
Closed

CS2 Discussion: Features: async/await #4908

coffeescriptbot opened this issue Feb 19, 2018 · 32 comments

Comments

@coffeescriptbot
Copy link
Collaborator


Migrated from coffeescript6/discuss#10
Originally created by @rattrayalex on Sat, 23 Jul 2016 02:54:17 GMT


The syntax here could be quite tricky, and the feature is pretty important. This could be an opportunity for CoffeeScript to offer dramatically nicer syntax than ES6 for a core feature.

Let's discuss anything related to async/await here.

@coffeescriptbot
Copy link
Collaborator Author


Migrated from coffeescript6/discuss#10 (comment)
Originally created by @DomVinyard on Mon, 25 Jul 2016 08:51:19 GMT


Implicit async if await is detected in the method body +1

@coffeescriptbot
Copy link
Collaborator Author


Migrated from coffeescript6/discuss#10 (comment)
Originally created by @carlmathisen on Mon, 25 Jul 2016 18:34:22 GMT


Agree with @DomVinyard on this one. This is what IcedCoffeeScript is doing.

@coffeescriptbot
Copy link
Collaborator Author


Migrated from coffeescript6/discuss#10 (comment)
Originally created by @octet-stream on Fri, 29 Jul 2016 22:51:51 GMT


Why can't we just use syntax with async and await keywords? Anybody, please, explain the reason.

@coffeescriptbot
Copy link
Collaborator Author


Migrated from coffeescript6/discuss#10 (comment)
Originally created by @rattrayalex on Tue, 26 Jul 2016 03:21:03 GMT


Ah, <= would clash with "less than or equal to" so that probably would not work.

@coffeescriptbot
Copy link
Collaborator Author


Migrated from coffeescript6/discuss#10 (comment)
Originally created by @DomVinyard on Tue, 26 Jul 2016 08:32:03 GMT


@rattrayalex i think if we were trying to mess with 'await' as a keyword, the only sensible 'true coffeescript' approach would be to go the other way and make it the more verbose 'wait for', rather than trying to get people to memorise a new operator.

fn() ->
  res = wait for fetch('url')
  console.log wait for res.json()

But that has a whole bunch of issues, not least of all the overloading on the word 'for'. Personally think should be left untouched.

fn() ->
  res = await fetch('url')
  console.log await res.json()

Is such a wonderful reminder of how far JS has come, isn't it.

@coffeescriptbot
Copy link
Collaborator Author


Migrated from coffeescript6/discuss#10 (comment)
Originally created by @rattrayalex on Tue, 26 Jul 2016 03:19:24 GMT


Personally, I find the await syntax a bit wordy for CS:

fn() ->
  res = await fetch('url')
  await res.json()

What about something like this instead:

fn() -> 
  res <= fetch('url')
  <- res.json()

Where <= compiles to = await and <- compiles to await. Of course, could make sense to just choose one or the other (probably <-, which is what Haskell uses) for both cases and infer.

Thoughts?

@coffeescriptbot
Copy link
Collaborator Author


Migrated from coffeescript6/discuss#10 (comment)
Originally created by @rattrayalex on Tue, 26 Jul 2016 03:17:21 GMT


Basic support is shipping in several browsers: tc39/proposal-async-await#97 (comment)

@coffeescriptbot
Copy link
Collaborator Author


Migrated from coffeescript6/discuss#10 (comment)
Originally created by @rattrayalex on Sat, 30 Jul 2016 18:21:29 GMT


@octet-stream why does async need an alias? (see my post just above, and others on this thread)

Note also this would prevent async => functionality, which would be very nice to have.

Slightly OT, but I also believe the do is unnecessary; in koa 2, next is a function, so it should just be <~ next().

@coffeescriptbot
Copy link
Collaborator Author


Migrated from coffeescript6/discuss#10 (comment)
Originally created by @rattrayalex on Tue, 26 Jul 2016 03:06:10 GMT


async functions don't necessarily contain await, eg;

async function () {
  return Promise.resolve(1)
}

... but, they also don't need the async keyword.

So I think I support implicit async as well.

@coffeescriptbot
Copy link
Collaborator Author


Migrated from coffeescript6/discuss#10 (comment)
Originally created by @rattrayalex on Tue, 26 Jul 2016 17:38:09 GMT


I am leaning in the direction of leaving await as-is as well. At least for v1. Would be relatively easy to add an optional <- operator later if people want it (I'm hoping async/await experiences really big adoption).

In a world with a <- operator, I assume it would be better to allow both <- and await, rather than enforcing only the former, correct? eg; coffeescript allows both == and is

@coffeescriptbot
Copy link
Collaborator Author


Migrated from coffeescript6/discuss#10 (comment)
Originally created by @DomVinyard on Wed, 27 Jul 2016 08:17:47 GMT


@rattrayalex

I'm hoping async/await experiences really big adoption

amen, brother. and

it would be better to allow both <- and await, rather than enforcing only the former

Yeah, absolutely. +1 here.

@coffeescriptbot
Copy link
Collaborator Author


Migrated from coffeescript6/discuss#10 (comment)
Originally created by @octet-stream on Sat, 30 Jul 2016 18:10:31 GMT


I'm not sure, but how about adding these operators as aliases for async and await?
Something like ~> for async and <~ for await
For example, the basic Koa app with this syntax:

  Koa = require 'koa'

  koa = new Koa

  async koa.use (ctx, next) ->
    ctx.body = 'Hello, world!'
    await do next

  koa.listen 2319, -> console.log 'Koa started on http://localhost:2319'

or

  Koa = require 'koa'

  koa = new Koa

  koa.use (ctx, next) ~>
    ctx.body = 'Hello, world!'
    <~ do next

  koa.listen 2319, -> console.log 'Koa started on http://localhost:2319'

@coffeescriptbot
Copy link
Collaborator Author


Migrated from coffeescript6/discuss#10 (comment)
Originally created by @rattrayalex on Sat, 30 Jul 2016 02:18:09 GMT


To elaborate, the async keyword can be unwieldy, and would be downright confusing on coffeescript:

async -> await bar()

which looks like you're doing this:

async(function(){ await bar(); });

which you're not.

CoffeeScript's two-character function definitions enable more expressive, functional programming styles, and make JavaScript (arguably) more readable and (absolutely) more lightweight. Requiring use of the async keyword would increase the character count by a factor of 4 (2 to 8, including the space).

Most importantly, whether or not a function is asynchronous can be inferred by whether it contains an await. This has strong precedent in CoffeeScript, which infers whether or not a function is a generator based on whether it contains a yield keyword.

The one reason one might want to include async is for explicitness, and I don't think that jibes with coffeescript's general mentality 😉

@coffeescriptbot
Copy link
Collaborator Author


Migrated from coffeescript6/discuss#10 (comment)
Originally created by @rattrayalex on Sat, 30 Jul 2016 02:26:41 GMT


As for "why introduce new syntax for await?" the primary answer again is brevity and convenience. Once async/await is mainstream, await is likely to be an extremely commonly used keyword, especially in node programs. Basically anytime one would consider calling a promise today, they would probably want to use await in the future.

So code that used to look like this:

foo() ->
  fetch(someUrl)
    .then resp -> 
      resp.json()
    .then json ->
      someTransform(json)

can now look like this:

foo() ->
  resp = await fetch(someUrl)
  json = await resp.json()
  someTransform(json)

or, better yet:

foo() ->
  resp <- fetch(someUrl)
  json <- resp.json()
  someTransform(json)

... which has a substantial keystroke savings, is arguably more clear/readable, and doesn't look like a function call.

In general, CoffeeScript is very light on keywords, which is great because function calls look like keywords.

@coffeescriptbot
Copy link
Collaborator Author


Migrated from coffeescript6/discuss#10 (comment)
Originally created by @GeoffreyBooth on Tue, 06 Sep 2016 02:11:31 GMT


Also, is there anything we lose by not supporting this feature? Any JavaScript library or framework that can’t be used because we can’t do await? Just trying to prioritize.

I noticed that async/await isn’t even standardized, not in ES2015 or ES2016. There’s no MDN page, and even the ECMAScript syntax could change.

@coffeescriptbot
Copy link
Collaborator Author


Migrated from coffeescript6/discuss#10 (comment)
Originally created by @rattrayalex on Sat, 30 Jul 2016 01:40:24 GMT


@octet-stream verbosity.

😉

@coffeescriptbot
Copy link
Collaborator Author


Migrated from coffeescript6/discuss#10 (comment)
Originally created by @octet-stream on Sat, 06 Aug 2016 21:57:53 GMT


Use facebook regenerator if you want working example :)

@coffeescriptbot
Copy link
Collaborator Author


Migrated from coffeescript6/discuss#10 (comment)
Originally created by @GeoffreyBooth on Tue, 06 Sep 2016 00:47:44 GMT


I’m working on the document that puts in one place our consensus plans. To confirm that I’m understanding this correctly:

  • We will not support the async keyword, or some symbol version of it; CoffeeScript will output async automatically as part of a function definition when it sees await inside the function, just like it outputs function* automatically as part of a function definition now when it sees yield inside the function.
  • Just like yield, we will use the await keyword inside the function (unless there’s consensus on <- or <~ or something else?).

So to take the example from http://stackabuse.com/node-js-async-await-in-es7/, this JavaScript:

var request = require('request-promise');

async function main() {  
  var body = await request.get('https://api.github.com/repos/scottwrobinson/camo');
  console.log('Body:', body);
}
main();

would be written in CoffeeScript as:

request = require 'request-promise'

main = ->
  body = await request.get 'https://api.github.com/repos/scottwrobinson/camo'
  console.log 'Body:', body

main()

? I think using await is the most Coffee-like considering we already have yield used in a similar way.

@coffeescriptbot
Copy link
Collaborator Author


Migrated from coffeescript6/discuss#10 (comment)
Originally created by @octet-stream on Sat, 06 Aug 2016 21:54:56 GMT


Yes, I think we can implicit async by detecting await into function.
This is just an example repository with async/await syntax support.

@coffeescriptbot
Copy link
Collaborator Author


Migrated from coffeescript6/discuss#10 (comment)
Originally created by @rattrayalex on Sat, 30 Jul 2016 18:25:00 GMT


<~ seems like an interesting alternative to <- for await. I like that it looks a little different from function definition. I'm not sure what I think of the fact that the two characters are at opposite corners of a US-QWERTY keyboard.

Other than the fact that <- has precedent in Haskell, and possibly others, I'm not sure I see much of an argument either way.

Thoughts?

@coffeescriptbot
Copy link
Collaborator Author


Migrated from coffeescript6/discuss#10 (comment)
Originally created by @GeoffreyBooth on Thu, 08 Sep 2016 05:15:10 GMT


@zeekay pointed out this PR which seems to be exactly what we want to implement, though it should be revised to remove the ES5 shim. Based on the precedent of generators and modules, I think our consensus now is that ESNext features are output as ESNext, especially if they’re opt-in by using a special syntax. But this feature may be very close to implementation based on the work in the PR.

@coffeescriptbot
Copy link
Collaborator Author


Migrated from coffeescript6/discuss#10 (comment)
Originally created by @zeekay on Fri, 05 Aug 2016 20:06:49 GMT


Prefer implicit async by detecting await.

  1. Least amount of syntax to add.
  2. Closely mirrors compiled JavaScript.
  3. Mirrors existing CoffeeScript support for generators.
  4. CoffeeScript as a language seems to lean towards plain language over symbols (as for operators).
  5. Will probably be less confusing to newcomers. In my mind at least, this trumps other considerations.

@coffeescriptbot
Copy link
Collaborator Author


Migrated from coffeescript6/discuss#10 (comment)
Originally created by @octet-stream on Sat, 30 Jul 2016 19:51:35 GMT


Slightly OT, but I also believe the do is unnecessary; in koa 2, next is a function, so it should just be <~ next().

I'm just used do keyword for function call without arguments. Like this:

sayHello = (name = 'Nick') -> console.log "Hello, #{name}!"

do sayHello # -> Hello, Nick!
sayHello "Alex" # -> Hello, Alex!

I think this code looks more pretty than sayHello() :)

why does async need an alias?

Maybe just for alternative asynchronous function declaration syntax?

@coffeescriptbot
Copy link
Collaborator Author


Migrated from coffeescript6/discuss#10 (comment)
Originally created by @kirly-af on Wed, 03 Aug 2016 18:54:29 GMT


I would definitely go for a simple function declaration as before. First, as said before it matches the way CoffeeScript manages generator (inference from the yield usage).

Second, it will not require you to care about removing async from the function declaration if you happen not use await inside anymore (though, this could be linted easily). Also, I imagine people would end up using it everywhere in their function declarations for no reason.

Oh, and concerning <- vs <~, I vote <-.

@coffeescriptbot
Copy link
Collaborator Author


Migrated from coffeescript6/discuss#10 (comment)
Originally created by @alangpierce on Tue, 06 Sep 2016 02:18:21 GMT


I noticed that async/await isn’t even standardized

It reached stage 4 a few months ago: https://github.com/tc39/proposals/blob/master/finished-proposals.md . So all of the details have been finalized and it will be in ES2017. My guess is that the "Debatable Syntax & Semantics" part of the page you linked is just out of date.

@coffeescriptbot
Copy link
Collaborator Author


Migrated from coffeescript6/discuss#10 (comment)
Originally created by @GeoffreyBooth on Mon, 12 Dec 2016 00:17:40 GMT


Sure, but please review what’s already been built so far regarding await. We would want to build on top of that.

@coffeescriptbot
Copy link
Collaborator Author


Migrated from coffeescript6/discuss#10 (comment)
Originally created by @mitar on Sun, 11 Dec 2016 20:20:09 GMT


One question. Is there ever a reason that developer would call an async function without await?

Because if there is not one, then we could really easily do something where we mark a function as async, which would do two things:

  • every call to it would use await automatically (no need to change anything)
  • every return from the function would wrap the return value into a Promise (this is OK, because wrapping Promise inside a Promise works)

So what I would like from CoffeeScript is to make all this magical. So maybe we could have:

fetch (url) ~>
  new Promise (resolve, reject) ->
    request url, (error, response, body) ->
      return reject error if error
      resolve body

Observe ~> (just a suggestion) as a way to define async function.

Here, a good thing is that a return value of the Promise's executor is ignored.

Now, I dislike the fact that we have to write new Promise all the time. One option could be that we overload the do operator:

fetch (url) ~>
  do (resolve, reject) ~>
    request url, (error, response, body) ->
      return reject error if error
      resolve body

So because do is defined with ~>, it is changed to a new Promise.

Alternatively, we could do (a list of different syntaxes):

fetch (url) ~>
  request url, (error, response, body) ->
    resolve error if error
    reject body

(Here resolve and reject become the CoffeScript provided keywords which map to the closest ~>'s resolve and reject. They also do return resolve. If anyone wants to do something after resolve, they should define their own Promise. If developer defines their own resolve or reject, those are used instead.

Alternative:

fetch (url) ~>
  request url, (error, response, body) ->
    fetch.throw error if error
    fetch.return body

Or:

fetch (url) ~>
  request url, (error, response, body) ->
    fetch.reject error if error
    fetch.resolve body

BTW, I think just having do (resolve, reject) ~> be mapped to new Promise (resolve, reject) -> could be a great improvement for anyone using Promises, no matter what you think about my other ideas.

@coffeescriptbot
Copy link
Collaborator Author


Migrated from coffeescript6/discuss#10 (comment)
Originally created by @rattrayalex on Sun, 31 Jul 2016 00:09:44 GMT


I'm just used do keyword for function call without arguments

Ah, silly me. Thanks.

why does async need an alias?

Maybe just for alternative asynchronous function declaration syntax?

What's wrong with just -> ? CoffeeScript can infer whether to compile to async function or function.

@coffeescriptbot
Copy link
Collaborator Author


Migrated from coffeescript6/discuss#10 (comment)
Originally created by @GeoffreyBooth on Sun, 11 Dec 2016 23:19:21 GMT


Sorry forgot to close this. This feature has been implemented.

@coffeescriptbot
Copy link
Collaborator Author


Migrated from coffeescript6/discuss#10 (comment)
Originally created by @mitar on Sun, 11 Dec 2016 23:56:53 GMT


Hm, OK, but what about discussion about making async/await more integrated with the language? Should I open another issue for that?

@coffeescriptbot
Copy link
Collaborator Author

From @mitar on Dec 11, 2016

If I understand, currently there is only await keyword added, and any function which is using await gets automatically async to its declaration. I will open a new issue.

@coffeescriptbot
Copy link
Collaborator Author

From @mitar on Dec 11, 2016

I opened coffeescript6/discuss#60.

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

2 participants