←back to thread

305 points kristoff_it | 7 comments | | HN request time: 1.272s | source | bottom
Show context
dvt ◴[] No.44610616[source]
"Asynchrony" is a very bad word for this and we already have a very well-defined mathematical one: commutativity. Some operations are commutative (order does not matter: addition, multiplication, etc.), while others are non-commutative (order does matter: subtraction, division, etc.).

    try io.asyncConcurrent(Server.accept, .{server, io});
    io.async(Cient.connect, .{client, io});
Usually, ordering of operations in code is indicated by the line number (first line happens before the second line, and so on), but I understand that this might fly out the window in async code. So, my gut tells me this would be better achieved with the (shudder) `.then(...)` paradigm. It sucks, but better the devil you know than the devil you don't.

As written, `asyncConcurrent(...)` is confusing as shit, and unless you memorize this blog post, you'll have no idea what this code means. I get that Zig (like Rust, which I really like fwiw) is trying all kinds of new hipster things, but half the time they just end up being unintuitive and confusing. Either implement (async-based) commutativity/operation ordering somehow (like Rust's lifetimes maybe?) or just use what people are already used to.

replies(10): >>44610771 #>>44610939 #>>44612125 #>>44612190 #>>44612605 #>>44612656 #>>44612932 #>>44613047 #>>44613470 #>>44615786 #
1. tsimionescu ◴[] No.44613470[source]
> So, my gut tells me this would be better achieved with the (shudder) `.then(...)` paradigm. It sucks, but better the devil you know than the devil you don't.

The whole idea behind `await` is to make the old intuition work without the ugliness of `.then()`. `f(); await g(); h()` has exactly the expected execution ordering.

replies(1): >>44613746 #
2. Yoric ◴[] No.44613746[source]
Can confirm.

In JS, we designed `await` specifically to hide `.then()`, just as we had designed `.then()` because callbacks made tracking control flow (in particular errors) too complex.

replies(1): >>44613861 #
3. psychoslave ◴[] No.44613861[source]
How is that any better to have await? Any resources I might consult on this?
replies(2): >>44613995 #>>44614028 #
4. Yoric ◴[] No.44613995{3}[source]
Well, one of the ways we "sold" async/await it to Google was by showing how we could improve Promise-based tests.

I recall that one of our test suites was tens of thousands of lines of code using `then()`. The code was complicated enough that these lines were by and large considered write-only, partly because async loops were really annoying to write, partly because error-handling was non-trivial.

I rewrote that test suite using `Task.spawn` (our prototype for async/await). I don't have the exact numbers in mind, but this decreased the number of LoC by a factor of 2-3 and suddenly people could see the familiar uses of loops and `try`/`catch`.

5. tsimionescu ◴[] No.44614028{3}[source]
Well, consider the difference between

  a().then(() =>
         b())
     .then(() =>
         c())
Compared to

  await a()
  await b() 
  await c() 
Even for this simple case I think it's much clearer. Then look at a more complex case:

  for( i=0; i<n; i++) {
    await a(i) ;
  } 
Now try re-writing this with then() and see the difference.
replies(1): >>44614462 #
6. psychoslave ◴[] No.44614462{4}[source]
For the first one, it really feels like only a matter of how you break lines. Sure there is also the matter of anonymous function syntax, but

The latter is more fair, here is a possible solution:

  const gen = (function* () {
    for (let i = 0; i < n; i++) yield a(i);
  })();

  const run = next => !next.done && next.value.then(() => run(gen.next()));
  run(gen.next());
Or something similar using reduce. But in both cases, it illustrates the point, I guess.

But if we are at point we can introduce new keywords/syntax in the language, it would just as well possible to come with something like

  a.chain(b, c)
In case you need to pass parameters

  a.chain([b, p1, p2], c)
And for the latter case

  const indexes = (function* () {
    for (let i = 0; i < n; i++) yield i;
  })
  a.through(indexes)
replies(1): >>44614609 #
7. Yoric ◴[] No.44614609{5}[source]
Well, sure, if you have `yield`, you pretty much have `await` already, as `await` is thin syntactic sugar on top of `yield` in all languages other than OCaml.