Hacker News new | past | comments | ask | show | jobs | submit login
Async JavaScript Generators (bignerdranch.com)
119 points by michaelsbradley on April 1, 2017 | hide | past | favorite | 25 comments



Generators are used to great effect in Ember thanks to the ember-concurrency[0] addon. It was far and away the most frequently talked-about topic at EmberConf last week. If you're interested, I'd highly recommend machty's talk[1] from last year where he introduced it. His talk from last week was also very good, but it does not appear to be available yet.

I don't think it's currently using async generators (have yet to read the linked article), but I suspect this new language feature may lead to some improvements!

0. http://ember-concurrency.com

1. https://vimeo.com/162329769


Also worth taking a look at redux-saga. ember-concurrency is nice and a good start, but redux-saga is a far more complete and testable solution for dealing with concurrency and side-effects anywhere you use redux (including ember).


Is it only usable as an ember addon in the browser?

It seems like this would be useful as a stand-alone library for node.js projects.


To the best of my understanding, yes, it's pretty well tied to Ember. Other options (such as redux-saga mentioned in a sibling comment) may be a better fit.


Every async operation can potentially fail. And one obvious way is: a timeout. It is very unlikely you want your async operation to last forever. So sometimes you need to specify that timeout and clean up everything afterwards (e.g: close handles to files or connections, unsubscribe event handlers, or anything necessary to not produce a leak), as well as log whatever happened and propagate errors in a controlled manner.

Having this said, what is very lacking in most programming blog posts is the error handling portion, which is important. Errors happen all the time, and nothing is 100% reliable. Hardware is unreliable, networks are unreliable, input is unreliable... And this needs to be reflected in your code.

Some communities understand that code snippets from blog posts are not production code. But doesn't seem to be the case in the node community.

The impact of that is that is that now:

- npm is full of modules that do not handle their own errors

- many node applications lack robustness, bringing back reputation to node. e.g: not taken seriously as a platform language just as a throwaway orchestration layer and doing monkeying with JSON.

- lack of error handling is mistakenly seen as elegance and simplicity. in reality it's lack of skill, seniority and responsibility

- node module descriptions have more happy emojis and hyped superlatives than actual useful descriptions of how error conditions are handled

- consuming a node module today is playing Russian roulette each time you upgrade a version

I really hope the community takes a turn towards making code more robust and that could start in the blogosphere, talks, etc. But javascript is now the lingua franca of people that do not care about error handling and that is sad.


Did any of the code in the article not handle errors? Everything was using Promises and async functions which automatically have error progration handled for you.


In the article, the errors may propagate but there is no error handling or timeout.


Totally agree as a former Node.js fanatic who got burned due to lack of my own understanding of error handling in Node.js.


Hey, here is a talk I gave about rewriting Rx with async iterators (a complex example is shown) - https://docs.google.com/presentation/d/1nAi7fTa2K9XUIDSG7Bg1...


Nice presentation. I have used Rx in some previous projects, however for new things I now also prefer pull-style APIs with possible async/await like you have shown. From my point of view they are easier to comprehend (more normal syntax instead of magic operators), easier testable (less callbacks in tests) and allow a better ingeration overall (e.g. you can move ownership of the object which is iterated upon between components without doing things like complete/unsubscribe and caring about whether it's a hot observable and unsubscription might loose events).

One remark about your slides: Your implementation of "throttle" might not be what one would expect from a debounce/throttle function, as it will never emit the last value if this is filtered (shortly occurs after a previous one). In most applications the last value of an eventstream is important, as it represents the most recent / final state. E.g. for the example which makes HTTP queries out of form data you would never make a query for the final text content if the last keystroke happenend in the debounce period for the previous one.


This is really neat. I liked how you showed it could all be done in one for loop. It really does make it seem much simpler than RxJS or the example you gave with the async generator utility functions. At the same time I do love reusable utility functions, but they have more mental overhead because of their abstract nature.


I'm curious whether the patterns explored have found application in a redux middleware[1] approach that builds on or is inspired by redux-observable[2] and/or redux-saga[3]. I've been searching around since reading the OP, but no joy so far.

[1] http://redux.js.org/docs/advanced/Middleware.html

[2] https://redux-observable.js.org/

[3] https://redux-saga.github.io/redux-saga/


Most of these patterns are eventually implemented in a production redux-saga application (at least in my experience, with React Native). Things like eventChannels are fairly similar, if not abstracted away a bit


i love js. but i feel js is getting more and more uglier by the day. in my early days, i would have been excited for all the cool new syntaxes that comes out, i for one wished some of the new syntaxes and changes we now have more than 8 years ago. but as i get older, i start appreciating the languages and frameworks that takes the simple and elegant route. the beauty of languages comes from simplicity. anyone can throw a bunch of things to make something work but takes lot of thought and pragmatism to make it look and feel simple.

i think the js fatigue is real and while async await is great and heading in the right direction, it will be some time and evolution for it to be where everyone will accept it as elegant.


Did you read the article


Yeah I agree with you here. I was amazed he was able to make a RxJS look a like in such few lines of code using async iterables. It looks like a really nice addition to the language for those who already love FRP concepts.


Streams can be implemented with functions as well, no need for async generators. See `flyd` or `mithril-stream`...


I also do think JavaScript fatigue is real though I so I agree with the top level comment too, but I think it's more due to the tooling overhead.


All I'm saying... Dart has had asynchronous generators for years.

Really awesome article, though, and very well-written.


And Dart async generators return Streams, the one streaming async primitive.

JavaScript async generators return "async iterators", which are like an iterator where next() returns a Promise.

But JavaScript, in browsers at least, but I assume node will follow, will also have a separate Stream class used by fetch() and other I/O, with a different API where read() returns a Promise.

And then, everyone seems to be clamoring for Observables, a third async streaming API.

This is not going to be a great situation, 3 different async streaming APIs, so I'm sure some enterprising young coder will create a 4th API to wrap the other 3 in a common interface.

Yay, JavaScript.


Hey there, I'm both the author of the browser streams spec [1] and the current champion of the async iteration spec [2] that this article discusses. We have, in fact, thought through the interaction here :).

johnsonjo got things basically right in his response. We go into more detail in the streams FAQ, including the connection to observables:

- "How do readable streams relate to observables or EventTarget?" [3]

- "How do readable streams relate to async iterables?" [4]

The basic punchlines are:

> Observables and readable streams both share the semantic of "zero or more chunks, followed by either an error or done signal". But beyond that, they are not very comparable.

and

> The best analogy is something like "readable streams is to async iterable as array is to sync iterable." That is, just like arrays are a specialized type of iterable optimized for memory locality and random access, readable streams are a specialized type of async iterable optimized for things like off-main-thread data transfer and precise backpressure signaling.

I do indeed expect us to start speccing the [Symbol.asyncIterator]() for readable streams, and shipping it (behind a flag at first) in Chrome, within the next month or so.

[1]: http://streams.spec.whatwg.org/ [2]: https://tc39.github.io/proposal-async-iteration/ [3]: https://github.com/whatwg/streams/blob/master/FAQ.md#how-do-... [4]: https://github.com/whatwg/streams/blob/master/FAQ.md#how-do-...


Async iterables seem to be more like a unifying feature to me.

For example the TC39 docs on async iterable say this: > Furthermore, we introduce a new symbol > used for obtaining an async iterator > from a given object, Symbol.asyncIterator. > This allows arbitrary objects to advertise > that they are async iterables, similar to > how Symbol.iterator allows you to advertise > being a normal, synchronous iterable. > An example of a class that might use this > is a readable stream. [1]

So, the readable stream may end up implementing this api. I went to see if the same thing might go for the observable api. I found that they may have the ability of coercing async iterables with Observable.from()[2] or they could have the ability for asyncIterators to be used as observables through other methods [3].

Observables do implement their own Symbol.observable which does make them different. Really Observables do play a different role than async iterables though[3]. Something that makes observables different is they don't have to be asynchronous. As you may have noticed in the article Promises are added to a queue as micro-tasks where as observables can and will execute immediately if they have a value ready.

Honestly we don't even know whether both or either of these will make it into the final spec. I personally hope they will make it. As long as there is strong interoperability it is fine with me, which as it is this seems to be the case.

[1] https://github.com/tc39/proposal-async-iteration#async-itera... [2] https://github.com/tc39/proposal-observable/issues/130 [3] https://github.com/tc39/proposal-async-iteration/issues/47 [4] https://github.com/tc39/proposal-observable/issues/116


The main differences between the three can be described along two axes: buffering/single-subscriber vs. non-buffering/multi-striver, and synchronous vs. asynchronous.

You don't really need three separate interfaces for this. Dart's Streams can cover all cases depending on how you create the source StreamController.

I asked some of the people behind the specs and they felt if was too complicated to have one interface cover the use cases. I tend to think it's more complicated now. I can't trivially write code to handle all interfaces without explicitly thinking and opting in to it.


Rauschmayer looked at the different roles back in Oct '16:

http://2ality.com/2016/10/asynchronous-iteration.html#altern...


Thanks for the link. Rauschmayer is a superb teacher through his blogs, I would definitely take his words over my own.




Consider applying for YC's Spring batch! Applications are open till Feb 11.

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: