Rethinking Asynchronous JavaScript

Concurrency + Channels

Kyle Simpson

Kyle Simpson

You Don't Know JS
Rethinking Asynchronous JavaScript

Check out a free preview of the full Rethinking Asynchronous JavaScript course

The "Concurrency + Channels" Lesson is part of the full, Rethinking Asynchronous JavaScript course featured in this preview video. Here's what you'd learn in this lesson:

CSP or, Communicating Sequential Processes, involves modeling concurrency through channels. These channels use blocking techniques to send and receive messages. After explaining some of the concepts behind CSP, Kyle shares a simple code example.


Transcript from the "Concurrency + Channels" Lesson

>> [MUSIC]

>> Kyle: The last pattern that I want to teach you is. And by the way, after I teach of this last pattern there's a last exercise. So I'm gonna teach you CSP in exercise nine. Exercise ten is a much more sophisticated application that I've built, it's called a tale of three list.

And your exercise there is going to be to go back and revisit either reactive or CSP in the context of a much more sophisticated application. So if you're looking for more practice on that stuff, it's coming with exercise ten. So don't feel like that's the last that we're gonna talk about it.

But I want to shift gears and focus on our last major pattern to introduce is called CSP. CSP is about modeling concurrency with channels. We've modeled concurrency now three, or four, or five different ways. This is another way of modeling concurrency. And channels, if you wanna think about what is a channel, a channel is kinda like a stream, it's kinda like a pipe.

Except there's one important characteristic and that is that the pipe has no buffer size and therefore has automatically built into it this notion of back pressure. So if you've done stream space programming before you've probably heard or read about this notion of back pressure. But to illustrate that I want you to imagine that I have two different ends of a hose.

One way over here and then one way over here, and there's two people on either end. And they don't see each other, they don't have any communication between each other. And then this guy on this side says I'm gonna start pouring some water down through the hose. And the guy on the other end has the water coming out and he's spraying it.

At some point the guy who's spraying the water says I don't want any more water. But he doesn't have a communication with this guy, so how is he going to tell this guy I don't want any more water? Well he's gonna cap off the hose so that on the other end no more water can go in because the hose is completely filled up.

That's back pressure. That's a reverse way of communicating up from consumer to producer to say stop producing cuz I don't want any more. Is everybody following me? This is like 50,000 foot view of stuff. Well there are places in our applications where we don't have a direct connection between producer and consumer.

Because we've intentionally kept them separate. But we do want to have a signal, some way of signaling to the producer. I don't wanna take any more data on this end, stop sending me stuff. In the RxJS world that's their distinction between hot and cold observables, by the way.

But converting between hot and cold observables is not quite as clean and easy, at least in my practice, as it might seem in theory. So another way of modeling that signal is to use a channel in a CSP system. Because the channel, as I said, is like a pipe that can only take one message at a time.

And it automatically has back pressure, and what that means is you can't send me something until I'm ready to take it. And I can't take something until you're ready to send it. Is everybody following me? There's no queue there. It's one message that transfers through the channel. So, it's this notion of blocking channels.

Now CSP, it stands for communicating sequential processes. This actually was a pattern invented way back in the 60s. Guy named Hoare, he wrote this book called Communicating Sequential Processes. It was a model for managing concurrency. Some of you may have heard of the actor model of concurrency, and there's a close but not direct relationship between the two.

The major difference with actors, at least from my research, the major difference with actors versus this thing that I'm about to talk to you about. With actors when you send a message it's asynchronous. You send a message and at some other point, somebody on the other end picks it up.

With CSP when you send a message it's very synchronous. At least the semantics are, it's very synchronous. When I wanna send a message there has to be somebody on the other end that's ready to take it. Cuz my hose can only accept one message at a time, okay.

So that's the major difference if you've heard about actors. It's the major difference between synchronous message passing with CSP versus asynchronous message passing with actors. So communicating sequential processes, what is what does this all about? It's about modeling your application with lots of tiny independent pieces that we call processes.

And if JavaScript had full
>> Kyle: like threading to it, if we could do like you can in Node where you can set up child processes and stuff. If we had full threading then every independent piece would be on its own thread and it would be running entirely independently of everybody else.

Well that's great because a lot of the times they are doing their own thing. But there are cases where these two independent pieces need to coordinate with each other. So you have two different parts of your application that are normally just kind of running doing their own thing.

But there is a state in which they get to where they say I need to communicate with this guy. And this guy gets to a state where he says I need some information from that guy. So whatever they've been doing independently, they get to a point where they say we need to coordinate.

And send a message in either direction, or perhaps both, one message this way, one message back. And then as soon as the messages have passed, they become unblocked and they go back to being independent. That's what we mean by processes, is that these things could in theory run independently.

Now we know within JavaScript, things aren't actually running independently like that. But we have seen something today that does sort of act like that. Does anybody know what I'm talking about?
>> Kyle: Didn't we see something today that sort of acts like it can just block itself and not affect any of the rest of the application?

>> Speaker 2: [INAUDIBLE]
>> Kyle: Generators, right? So what if I had a bunch of different generators that were all running in different places, and they were running independently with each other. And then at some point two generators said hey, we need to coordinate with each other. So let's have a communication channel where we can both block each other, both block waiting for the other one to show up.

And as soon as both of us of showed up we can send a message back and then we can go back to being independent. That's really the conceptual basis for CSP. And while that sounds fantastically more complicated, it is. It's also fantastically more powerful. Because we can literally model every independent piece of our application totally separate of everything else.

And all we have to think about is when I need a message I wait for the message. Or when I need to send a message I wait for the sending of the message. And other than that we have no understanding or knowledge whatsoever of any of the other pieces of the system.

So theoretically the implementation could take each one of those little processes and put them on an entirely separate thread. And the application would still work exactly the same way. This model for concurrency, the CSP model for concurrency, is so powerful. It is the model for concurrency in the Go programming language.

It's also what Clojure and ClojureScript uses. So I'm not, it's not just me making something up and saying it's cool. So really, really, really, super smart people have said this is the way to do concurrency. And in Go they do have threads so every Go, it's called a Go routine, every Go routine does end up spinning up on its own entirely independent thread.

It's one of the magic reasons why Go has so much efficiency, so much performance to it. Because I'm able to spin up all these different pieces of my application in separate threads. But I have a really simple way of communicating between the two of them through blocking channels, okay.

So let me try to illustrate in code what that looks like. I make a channel like I do up here on line 1. And then I have this process1, which is a generator cuz we're expressing this in terms of JavaScript. He wants to send something down the channel, so he uses a method called put, and he says this is the channel I want to send it on.

So we pass channel is the first, and then we pass the next argument is a message that we want to send. You notice I'm using yield in front of put, because I want for the process1 generator to pause. Until somebody on the other end of that channel says I'm ready to take that message.

So I say yield put, which locally blocks the process1 until somebody else comes along and unblocks it. Then I have this other process in my application, maybe in an entirely different part of the app. He's running along and he says I need a message from the ch channel.

So he says I'm gonna yield on a take, like I'm doing on line 10. It does not matter who gets there first.
>> Kyle: That's what's cool about it. I don't have to worry about whether or not there's a message waiting for me or not. I just say I wanna take a message, and I'll wait until one's ready.

Maybe it's ready right now, great. If it's not, I'll just patiently wait. I wanna push a message down. I don't have to know whether or not somebody's ready to get it or not. I'm just gonna wait until somebody's ready to take my message. That's what lets them be so independent.

Because the semantics of this communication channel take care of that communication, that back pressure, for us. So whenever process2 gets to line 10 and it says I'm ready to take it, immediately that message transfers through the channel. And process2 is unblocked, and process1 is unblocked, and then they just go on about their day.

And then we get to line 5 and we say wait a minute, I need a message from the channel. So I'm gonna wait again. And then process2, he's running along and hey says hey, I've got a message to send on the channel. So I'm gonna wait until somebody's ready to get it.

And he sends a message back up the channel. And then process1 is unblocked, and then process2 is unblocked, and they go on about their day. So you can have hundreds of lines of totally independent code that doesn't have to worry about any other piece of the system. And the only part that has to worry about it is when you know I've got a channel that I need some data on, or I've got a channel that I need to push some data along.

I'm just gonna wait for somebody to tell me that's okay. And underneath the covers this abstraction, the CSP paradigm is what manages all that complexity for you.
>> Kyle: Okay, now the really, really smart folks in Go, and Clojure, and ClojureScript, they're pretty convinced this is the most powerful model invented to date for concurrency management.

And I'm not one to bet against them, I think they're right. I do think this is the most powerful pattern that I've ever seen or heard anybody talk about for concurrency management. I'm fascinated by it, by the way. I've spent the last year researching how to understand this deeper and seeing practical ways of applying it to my user interfaces.

And I've spent a year at it and I still feel like I'm barely scratching the surface compared to these smart people. That whole standing on the shoulders of giants, I'm like clinging on for dear life. I don't even hardly know the stuff that they know. But I'm convinced that there is a whole lot here that we haven't even really uncovered and scratched the surface with.

So ClojureScript, guys that maintain ClojureScript, specifically a guy named David Swanadet, I'm sorry David Nolan who goes by Swanadet on Twitter. Super brilliant guy, every time I hear him talk I'm just like how did I even, he just drops down this knowledge like it's so easy and I'm learning so much.

But he's got a UI framework called Om, O-M. And its ClojureScript, which ClojureScript is ported to Java, it's ClojureScript is Clojure that can run in JavaScript in the browser. And they use this CSP all over the place, it models the entire user interface in CSP. And he produces this, the most amazing and most performant user interface I've ever seen.

It's incredible. So I'm convinced that there's a whole lot of, we haven't scratched the surface of the capabilities of it. And that's why I'm trying to convince people to think about this as well. This is another one of those tools to start to model our applications around.

Learn Straight from the Experts Who Shape the Modern Web

  • In-depth Courses
  • Industry Leading Experts
  • Learning Paths
  • Live Interactive Workshops
Get Unlimited Access Now