Go-like channels in 10 lines of JavaScript
41–50 of 65 posts
Re: Go-like channels in 10 lines of JavaScript
#42Earlier quoted context omitted.
I actually think async/await is a terrible primitive, and if you’re on Java, the approach of Project Loom is vastly superior (i.e. threads can be cheap so everything can just be synchronous again). I’ve no problem with library-level concepts like promises and futures but it feels very short sighted to put it into an actual language, it’s just noise.
Why don't you like async/await? 8 years ago I wrote a npm package that turned code that looks like this: tcp.send("syn", function(syn) { console.log("received", syn); tcp.send("syn-ack", function(synack) { console.log("received", synack); tcp.send("ack", function(ack) { console.log("received", ack); }); }); }); into this: seq([tcp, console], function(tcp, console) { var syn = {}; var synack = {}; var ack = {}; tcp.se…
If you put while (true) { } in an asynchronous function, wouldn't you also have a resource starvation problem? For what it's worth though, Loom threads are planned to be fully preemptible.
Re: Go-like channels in 10 lines of JavaScript
#43It's a bit out of fashion, but same thing can be achieved without any extra code using a built-in EventEmitter: let channel = new EventEmitter() await Promise.all([ compile.browser(channel), compile.server(channel) ]) // in compile.browser channel.emit("manifest", assetsManifest) // in compile.server channel.once("manifest", (assetsManifest) => ... ) A new emitter is used each time, so the end result is the same. Cur…
Re: Go-like channels in 10 lines of JavaScript
#44There are a ton of other approaches that would work with NodeJS that would be even more simple though right? The easiest or first that comes to mind is just have your function that does whatever functionality is required for creating this assetsManifest (whatever both compileServer and compileBrowser depend on). Lets call this createAssetsManifest. Then you have your 2 compile async functions but you just don't await…
The manifest is an artifact produced by the browser compilation, but it can be shared _before_ the browser compilation writes its results to disk. I _could_ refactor to have "browser compilation phase 1", then assets manifest, and then "browser compilation phase 2", but that's not how I model it in my head. Plus it would mean a diverging interface for `compileBrowser` and `compileServer` which doesn't fit my mental m…
I think this is the central matter when it comes to primitives for asynchronous programming.
There exist many ways we can think about async tasks. The JavaScript ecosystem provides for multiple. i.e., event callbacks, async/await, generators, and more.
Programmer reach for tools which best match how we have learned to model these problems in our heads.
It's okay for people to use what works best for them.
Re: Go-like channels in 10 lines of JavaScript
#45Earlier quoted context omitted.
> But there is no concurrency in Javascript! Yes and no. At the “ECMAScript”-level, JavaScript has no built-in concurrency, but every serious JavaScript implementation enables proper concurrency via the async primitives and the internal scheduling. Put simply, if you know how to write async code properly, your JavaScript code can achieve high concurrency!
No. All the JS code is still executing serially in the same event queue. This is a good thing, lean into the guarantees it provides.
Re: Go-like channels in 10 lines of JavaScript
#46The real essence of Go channels is their ability to participate in the built-in select keyword. Of course, Go does not have access to any magic CPU instructions that make it something Go can uniquely do. But anything that wants to be "Go channels but in X" need to be implementing select, not a send operation. Send operations are easy. And a useful primitive! Way back in the day, Queue in Python was the way to communi…
A few days ago, I ported ocaml/Event [2] to JavaScript, which provides concurrent ML-style synchronization operations.
It is possible to implement `Channel` and `select` in JS, but it is not easy to provide an idiomatic API and integrate it with the Promise ecosystem.
[1]: https://github.com/dhcmrlchtdj/sync-op [2]: https://ocaml.org/api/Event.html
Re: Go-like channels in 10 lines of JavaScript
#47This needs a comparison with streams, as opposed to promises. Streams are what you would use to achieve this in Node.js land. https://nodejs.org/api/stream.html
Re: Go-like channels in 10 lines of JavaScript
#48Earlier quoted context omitted.
Its a Typescript-ism for asserting that the value is not `undefined`. So you're telling the typechecker to trust you on this one
Thank you, didn’t know about this one!
Now that you do know about it, please use it sparingly if at all, i.e. when you’re absolutely sure you know more than the type checker, or when you’re in a context where it’ll be caught by other means. My typical lint setup disallows it in source code without an explanatory comment, and allows it in tests under the assumptions that either they’ll fail if wrong or that a reviewer will call out the test as overly complicated.
Re: Go-like channels in 10 lines of JavaScript
#49I would attack this one of two ways, both of which I feel are more idiomatic than trying to emulate Go in JS:
1) Factor out the intermediate computation and promise:
const manifestPromise = buildManifest();
await Promise.all([
compileBrowser({manifestPromise});
compileServer({manifestPromise});
]);
2) Just return the two promises from compilerBrowser(): function compilerBrowser() {
let resolveManifest;
const manifest = new Promise((res) => resolveManifest = res);
const result = (async () => {
// Compute manifest and resolve it before the final result:
resolveManifest(manifest);
// Compute the result of the result...
return finalResult;
}());
return {
manifest,
result;
};
}
IOW, decompose things into smaller pieces (1) and/or compose them into values that match what you need (2) and remember that a function can return a group of promises instead of a single one.Re: Go-like channels in 10 lines of JavaScript
#50Hmm, the way I see it is that this only makes a difference if your "more work" is async, in which case I would consider if the function needed refactoring instead - not possible to say with toy code of course. Something like: await makeManifest().then(manifest => Promise.all([moreWork(manifest), compileServer(manifest)])) If, however, "more work" is synchronous then the promise would not resolve until the next tick w…