Off the top of my head, that's certainly possible in Rust, C++ and Java - probably many others I just don't know about.
That particular bit of concurrency goodness comes from the 70s, so you can be sure other languages picked it up.
But does that matter? In all my experience, I can't think of any instances where that wouldn't be a distinction without a difference. (Perhaps I'm missing something, though.)
Where languages differ is what they make easy to use, or first class.
As an example consider Go's autoformatter gofmt. It got released with the language, pushed and marketed as the way to format your code - first class. As a result, virtually all Go code is uniformly formatted. Contrast that with autoformatters in other languages pre-Go. They existed, but often multiple per language, each with hundreds of configuration options, all with minimal uptake in their ecosystems.
(If your point was about it being _built into the language_, then it definitely stands, see the other discussion in the parent comment thread)
I guess I just like it being built into the language. Plus no function coloring (everything is asynchronous, but appears synchronous) really makes it easy to build complex systems.
https://www.boost.org/doc/libs/1_35_0/doc/html/boost_asio/tu...
(of course it was possible before)
This example sets up two timers which will regularly push events in the queue, while two threads processes these events. It's from before c++ had lambdas - nowadays it can be written much more tersely.
I'll try to write a short example after when I'm not on my phone - it should be reduceable to 5-6 lines of setup at most
#include <boost/asio.hpp>
#include <atomic>
#include <iostream>
#include <thread>
int main() {
boost::asio::io_context io;
using work_guard_type = boost::asio::executor_work_guard<boost::asio::io_context::executor_type>;
work_guard_type work_guard(io.get_executor());
std::vector<std::jthread> t;
for(int i = 0; i < 10; i++)
t.emplace_back(std::jthread([&io] { io.run(); }));
std::atomic_int counter{};
while(counter < 100)
io.post([&] {
std::cerr << std::this_thread::get_id() << '\n';
counter++;
});
io.stop();
return 0;
}That's a trivial thing in javascript.
var a = new Queue();
var b = new Queue();
a.read(() => some callback);
b.read(() => some callback);
And code continues here without blocking.
To be honest, I think you could do the same in any (slightly) modern language.
Agreed, and things happening "at the same time" are possible only with parallelism. Hence my question =)
But sure you could also add parallelism using a few built-in Node modules (there's cluster and worker threads, also don't know if atomics are already in Node, they are in V8),
It's not trivial anymore but also not that hard. A newcomer could figure it out in ~30 mins from the docs.
During my PhD we had to crunch some numbers and my group usually makes use of C++ with libs for that. I tried doing it in Node using cluster and let it spread through our whole cluster of nodes (funny how it plays on words) and performance was worse obv. but not that bad, it was about 2x slower, but with that I was able to write code and test it in hours instead of days, and it didn't matter to me if a program that was going to take 2 hours was now taking 4 as I was going home anyway and check it out next morning ¯\_(ツ)_/¯. Plus, my experiment had a REST API for free (well, like 8 lines of code) and when I showed that to them they were :O.
As far as I know, no other popular language allows you to read from multiple queues at the same time, which is an extremely useful pattern in concurrent programming. The only way to "select" from multiple sources in other languages is to use some kind of poll/select syscall on file descriptors - and even that is very limited, since kernel (at least Linux) does not allow userspace programs to create private file descriptors for intra-process communication - they must always be bound to some file/pipe/socket.