News for hackers. Stuff that matters.

Mitranim

Re:Show HN: Cancelable async primitives for JavaScript on Sunday August 20, 2017 @07:25PM (#15060766)
Added more examples and motivations for cancelation: https://github.com/Mitranim/posterus/blob/17c89694ecdce4633f...
Re:Show HN: Cancelable async primitives for JavaScript on Thursday August 10, 2017 @04:55AM (#14977772)
It sounds like you're saying "explicit FSMs are strictly more powerful than promises/coroutines". Which is true. However, they're inherently difficult to program. In my view, the key to simplicity and correctness of programs is reducing the amount of states the program can possibly have. Isolating a piece of code into a blocking sequence seems like a fairly good way of doing it.

Not sure what you mean by "simple examples without error handling". Just like normal synchronous code, promises and coroutines use exceptions, which have the nice property of composability: one catch for several statements/promises. So error handling is the same as in synchronous code.

Now granted, there's a LOT of async/reactive problems where sequential abstractions like promises are irrelevant. GUI programming comes to mind, where you want the view to reflect some reactive state that changes in arbitrary order.

Re:Show HN: Cancelable async primitives for JavaScript on Wednesday August 09, 2017 @08:51AM (#14968118)
That's what the mapping operators are for: `.mapResult` (same as `promise.then`), `.mapError` (same as `promise.catch`), or `.map`. The latter has an errback signature, like Node.js callbacks. They can also return new futures, transforming the result asynchronously. On top of that, error handling in promises/futures is much easier than in callbacks, as you can use one error handler for a chain of operations. Kinda like exceptions in synchronous code.
Re:Show HN: Cancelable async primitives for JavaScript on Wednesday August 09, 2017 @08:49AM (#14968101)
Glad it works for you!

Kind of answered in another subthread: https://news.ycombinator.com/item?id=14963524

Re:Show HN: Cancelable async primitives for JavaScript on Wednesday August 09, 2017 @08:45AM (#14968060)
Interesting! Nice that the approach is known, and others are being discussed. I should check GTOR again.

Multicast cancelation based on refcounting doesn't look right to me. I explicitly avoided this in Posterus, sticking with exclusive ownership, what GTOR calls unicast (thanks for linking). The reason is that subscribers may come and go over time. I've seen, and written, code that caches a promise and reuses it many times. It may start with one consumer, lose it, be canceled, then another consumer comes and gets rejected. This just feels like a big footgun. In Posterus, multicast is explicit and opt-in (`.weak()` futures).

If we eventually standardise a solution, I'd like something with as few interfaces as possible. In a dynamic language, you don't have the luxury of creating a future that initialises into a task, returning a cancelation token. Too many types without static checking is just a footgun. This is part of why Posterus only has futures, why cancelation is provided in the future constructor and tied to the future itself, and why they're eager rather than lazy. Deviating from these constraints would introduce new types, increasing the footgun potential.

Re:Show HN: Cancelable async primitives for JavaScript on Wednesday August 09, 2017 @06:12AM (#14967111)
This is news to me. Last time I tried RxJS, was unable to get a usable core less than 100 KB minified (I don't use gzip as a metric). Would consider 20-30 KB. Might want to look again.

Bundle size: a typical SPA imports multiple libraries, they import more libraries, and so on. It's not just Rx. I shouldn't have to explain how small things add up. Not caring about size is how it balloons up. We have to care about it in every library.

Re:Show HN: Cancelable async primitives for JavaScript on Wednesday August 09, 2017 @06:06AM (#14967084)
For one operation, sure. It doesn't work when you want to compose multiple async operations, wait until all are finished, or race several of them to completion (e.g. useful operation competing against timeout). The purpose of promises/futures is this composability that allows you to get the order and timing of operations right.
Re:Show HN: Cancelable async primitives for JavaScript on Wednesday August 09, 2017 @06:00AM (#14967054)
No disagreement here. Rx observables are strictly more powerful than promises/futures/streams.

But why start with the superset? I don't think it's good design. Consider the perspective of a language designer. You want to start with primitives that are as simple as possible while satisfying a vast number of real use cases. One-short async primitives are an important intermediary step towards observables, it shouldn't be skipped. Observables should be implemented in terms of these.

This layered design gets you a smaller cognitive cost of entry (promises are hard enough as it is!) and higher efficiency for the vast number of cases that don't need stream-like functionality.

On top of that, one-shot primitives are conceptually compatible with blocking expressions in coroutines such as async/await, whereas streams are not.

I don't get it when people shoot for fat primitives that do it all.

Re:Show HN: Cancelable async primitives for JavaScript on Wednesday August 09, 2017 @05:35AM (#14966923)
This is news to me. Last time I looked into RxJS, I was unable to get a minimum useful core of less than 100 KB minified. Might want to look again, thanks!
Re:Show HN: Cancelable async primitives for JavaScript on Wednesday August 09, 2017 @05:31AM (#14966904)
Correct. Most websites are better off with static HTML or server-side rendering and the least possible amount of JS. However some apps DO have to be fat. And don't forget about Node.js.
Re:Show HN: Cancelable async primitives for JavaScript on Wednesday August 09, 2017 @05:30AM (#14966896)
Doesn't sound as ergonomic as just returning a cancelation function in a future initialiser. Not seeing any other advantages, either. Am I missing some?
Re:Show HN: Cancelable async primitives for JavaScript on Wednesday August 09, 2017 @05:25AM (#14966878)
Agreed, often it doesn't matter. Sometimes it DOES matter (high latency / low bandwidth / weak device), and then your program is slower than it could be. Why not pursue async primitives that let you manage resources properly at no downside?
Re:Show HN: Cancelable async primitives for JavaScript on Wednesday August 09, 2017 @05:23AM (#14966871)
We need a standard destructor interface instead of choosing a different word every time. Cancel, close, destroy, drop, unmount, they do the same thing. If we settled on ONE destructor interface, we could have automatic resource management. [1] I use `deinit` in all my libraries, as it seems to be the most neutral word appropriate for every case.

`arrive` — no particular reason. It's one "errback" method rather than two methods like `resolve/reject`, so it needs to have a neutral tone. Not too happy with it, better suggestions are welcome.

[1] Basic implementation of automatic resource management in JS: https://mitranim.com/espo/#-agent-value-

Re:Show HN: Cancelable async primitives for JavaScript on Wednesday August 09, 2017 @05:17AM (#14966837)
Depends on use case. Sometimes you're surprisingly resource constrained.

People gave a few examples in this subtopic: https://news.ycombinator.com/item?id=14962684

Re:Show HN: Cancelable async primitives for JavaScript on Wednesday August 09, 2017 @05:15AM (#14966832)
People and programs change their minds all the time. A lot of behaviors we consider intuitive rely on some form of cancelation.

People gave a few examples in this subtopic: https://news.ycombinator.com/item?id=14962684

Re:Show HN: Cancelable async primitives for JavaScript on Wednesday August 09, 2017 @05:04AM (#14966767)
It would be race-free if `.then()` was invoked synchronously when evaluating the `await` expression, just like in normal Promise-based code. Currently in V8, there's a delay between evaluating `await` and actually calling `.then()`. If the promise uses a sufficiently nimble scheduler (i.e. based on `process.nextTick`), its unhandled rejection handler may run _in between_, throwing an exception, polluting stderr and possibly killing the process.

Example with Posterus:

  async function main() {
    try {
      await Future.fromError('fail')
    }
    catch (err) {
      console.error('caught:', err)
    }
  }
In Node, this actually produces an unhandled rejection because Posterus's scheduler uses `process.nextTick` and squeezes into this unnecessary delay. Doesn't happen if you `.catch()` manually or just use Posterus coroutines instead of interoping with async/await, but it highlights the incorrect implementation of async/await in the first place. (Or is the spec at fault?)
Re:Show HN: Cancelable async primitives for JavaScript on Tuesday August 08, 2017 @08:34PM (#14964455)
Not familiar with cancelation tokens. Can you describe the approach?
Re:Show HN: Cancelable async primitives for JavaScript on Tuesday August 08, 2017 @08:31PM (#14964433)
Not good enough. Each ongoing request eats machine resources and counts towards the browser request limit (6 or so). Cancelation must release the underlying resources, which XMLHttpRequest allows you to do. By being promise-based, fetch is fundamentally broken. It's a dead-on-arrival tool.
Re:Show HN: Cancelable async primitives for JavaScript on Tuesday August 08, 2017 @08:29PM (#14964415)
Yes. Depends on client bandwidth and CPU, but it's significant. In fact, it's insanely high. Not caring about "just another 100 KB" is how people end up with 2-5 MB bundles that take seconds to download on weak networks and seconds to execute on weak devices.
Re:Show HN: Cancelable async primitives for JavaScript on Tuesday August 08, 2017 @06:11PM (#14963397)
I guess this needs a bigger answer.

A well-designed tool should minimise the amount of states the program can be in. Asynchronous programming is already horrifically bad, it generates combinatorial explosions of intermediary states. Adding imperative, mutative programming and an event-driven API only exacerbates the problem, increasing the amount of possible state sequences even further. I don't think xtream provides a good API.

Re:Show HN: Cancelable async primitives for JavaScript on Tuesday August 08, 2017 @05:58PM (#14963278)
Glad you understand the problem. This is what futures are good for. Write an XMLHttpRequest adapter that returns a future, have an easy time composing or aborting operations.
Re:Show HN: Cancelable async primitives for JavaScript on Tuesday August 08, 2017 @05:54PM (#14963228)
Futures automatically coerce to promises, so yes.

Even better, they come with generator-based coroutines that work with futures, and are automatically cancelable: https://github.com/Mitranim/posterus#routine

You can even run it with Koa 2 instead of async/await: https://github.com/Mitranim/koa-ring

Re:Show HN: Cancelable async primitives for JavaScript on Tuesday August 08, 2017 @05:52PM (#14963213)
I believe we need a hierarchy of primitives:

Level 0: one-value operations (promises = futures).

Level 1: multi-value operations (streams = observables), implemented in terms of one-value operations. Tokio did it well for Rust (https://tokio.rs).

Starting at level 1 doesn't feel right to me.

Re:Show HN: Cancelable async primitives for JavaScript on Tuesday August 08, 2017 @05:46PM (#14963152)
In my experience, most async operations fit very well within the small feature set of promises/futures, with only a few operators (map/all/race). Streams should be the optional next layer of abstraction, not the only layer of abstraction. They should be built on top of futures. I quite like how Tokio did it for Rust futures. [1] Not a fan of Rx and similar designs.

There are also different types of observables to consider. Rx, xtream, Most.js, they're basically stream libraries. When doing GUI programming, it's more useful to have reactive units with a _synchronous_ data access, because it matches how you want to access the data when drawing your view (e.g. in React). I ended using observables that look like Clojure's atoms (see Espo: [2]), with a special adapter for React (see Prax: [3]). Observable libraries like Rx were useless for that use case.

[1] Example of decent future/stream design: https://tokio.rs

[2] Observables focused on synchronous access: https://mitranim.com/espo/#-atom-value-

[3] React adapter for implicit reactivity, based on synchronous observables (impossible with async streams): https://mitranim.com/prax/

Re:Show HN: Cancelable async primitives for JavaScript on Tuesday August 08, 2017 @05:19PM (#14962823)
Here's some examples from my experience.

* Server. Request handler starts expensive work. Let's say 4-5 database requests, some FS operations, rendering and sending response. Client disconnects. Running those operations will waste resources, we should stop. [1]

* Web. You can make at most 6 concurrent HTTP requests. They're precious resources. Dropping a request you no longer need will let others complete. It's nice to be able to abstract a painful ajax API behind something like a promise that doesn't lose the important ability to abort.

* React. View instance starts async fetch that eventually updates its state. User navigates to another view before it finishes. Updating the state after the component is unmounted is an error, and React will rightly complain. We should stop it.

* Server: abstracting an operation that's already cancelable behind a future. Let's say using Electron to render a website into a PDF. It's a really expensive operation, and the API is a pain to program against. You want to provide something as simple as a promise, but that doesn't lose the ability to stop it. [2]

[1] I write servers using Posterus coroutines, so all async code is automatically owned and canceled where appropriate: https://github.com/Mitranim/koa-ring

[2] This relies on futures to abstract away tricky timing management: https://github.com/Mitranim/epdf/blob/1c54481d4760a7eb730eb3...

Re:Show HN: Cancelable async primitives for JavaScript on Tuesday August 08, 2017 @05:08PM (#14962715)
Good suggestion, should probably address this in the readme.

Short answer: observables do too much, and Rx is WAY, WAAAAY too large. We need simple async primitives before layering bigger abstractions on them. They shouldn't take encyclopedic amount of reading to learn. They need to come in a library that doesn't weigh over 100 KB.

Re:Show HN: Cancelable async primitives for JavaScript on Tuesday August 08, 2017 @05:01PM (#14962666)
We're not competing with VMs here. From what I hear, like many other built-ins, "native" promises in every VM are implemented in JavaScript. They're not always well done (V8 promises were really bad for a while), and pay a mandatory overhead for useless "privacy" features dictated by the spec.
Re:Show HN: Cancelable async primitives for JavaScript on Tuesday August 08, 2017 @04:53PM (#14962586)
Pretty much what jrs95 said. If I understand the question correctly, `Future.all` and `Future.race` address exactly this case.

[1] https://github.com/Mitranim/posterus#futureallvalues

[2] https://github.com/Mitranim/posterus#futureracevalues

Older »