Transcript from the "Sequences & Gates" Lesson
>> Kyle Simpson: One abstraction that I've observed as particularly useful is to think about things in terms of a sequence, a sequence of events. My definition for a sequence is that it's a list of automatically chained promises. We know that we can chain promises together, I've shown you how to do that.
[00:00:20] It's pretty straightforward once you learn it. Five minutes, you're an expert at it like the rest of us. But as you start doing that, dozens and hundreds and even thousands of times in a project, you start to wonder gee, do I really have to create and return another promise?
[00:00:34] It gets a little bit tedious, it gets a lot tedious. Years ago, I was an early adopter of some of these concepts. I was working on promises before we had a name for them. Like five, six years ago, prenode, we were working on this concept of how to bring this stuff and I was an early experimenter in this.
[00:00:53] And I decided that one of the big problems was, I don't want to have to remember to create and chain a promise when my assumption should be, I'm always gonna do another step. Why not make it automatically create and chain promises for me. So my early experiments into this five, six years ago, before we had a name for promises, long before any of the existing libraries that you've heard of were around, is I was creating a library for that abstraction.
[00:01:18] And that library has gone through a lot of revisions and several name changes. It eventually arrived at the name that it currently goes by, which is asynquence which is the word asynch plus the word sequence smushed together, mostly cuz there are no other good names left [LAUGH] basically.
[00:01:36] Gotta come up with a unique name and there were no other good names, but it does speak to what its purpose is. Its purpose is to present you that abstraction, that notion of a sequence of a series of automatically chained promises. You'll notice that I can do a chain just like when you do with a promise chain, but there are some important differences here.
[00:01:54] On line 2, you'll notice that my then function is passed in function callback that I call done, just by convention, I call it done. That's our resolution trigger. So whenever that step on line 3 is complete, it calls done and that's what moves the sequence onto its next step.
[00:02:12] The promise to handle all that was created behind the scenes and automatically chained in so I didn't have to worry about that part. I only needed to worry about when to signal that it was time to move on to the next step. So their promises are absolutely happening under the covers but it's abstracting some of the tedious parts of that API.
[00:02:31] It's covering up some of the rough parts, it's doing error handling in a more sensible way. Basically, the design of asynquence was to make promises easier to reason about, easier to teach. That was the original design goal. At this point, asynquence is actually quite a bit more than that.
[00:02:49] It still has the same notion of a sequence and whatever, but I actually don't really use the sequence nature of asynquence that much. Cuz these higher order patterns are also, I think, much more interesting, the stuff that we're gonna get to through the end of the workshop. But at its heart, all it takes to make a sequence is to start chaining steps together just like you would with a promise and letting it do some of that work for you.
[00:03:12] So line 2 is a .then, that's just a regular step. But line 5 shows you that I can create a gate step just like I can do a promise.all. But rather than having a function that I return a promise.all from, which is super tedious, I just name my steps based upon what they do.
[00:03:29] So in this API, it's called .gate and there's an alias to .all if you prefer the word all to gate. You can use whichever one you want, but it does exactly the same thing. It takes those two or however many functions and runs them in parallel. Whenever all of them successfully complete, it moves on.
[00:03:51] I am not going to try to convince or sell you that you should switch to using asynsequence. However, I designed asynquence to make this stuff easier to teach. So for the balance of this workshop, all the exercises that we're going to do are going to rely upon using asynquence.
[00:04:09] So you only have to learn it just enough to get through these exercises and if you never want to use it again, that's totally fine. But it was important that we have one thing to learn rather than having to learn 15 different libraries to do this stuff. So asynquence will handle all the stuff that we will talk about through the rest of the workshop.
[00:04:26] It's why I'm giving you a brief introduction to the way that it works. [COUGH]
>> Kyle Simpson: Just as an example back to our silly calculating the meaning of life. Here's asynquence's version of meaning of life and you'll notice on line 8, I'm using a step called a waterfall. That's another higher order abstraction that we could define.
[00:04:49] What a waterfall is is still a sequential series. It's not gonna do stuff in parallel, still a sequential series, but it automatically collects the output from each step and passes it on to the next and the next and the next. So by the end, you have all the outputs from all the steps.
[00:05:04] You don't have to manually collect all those and pass them along, it automatically does that for you. So at the end on line 12, we get both num1 and num2 from the two previous series steps. That's what I call a waterfall. Something I was doing fairly often and I was doing very manually with other variables and I said let's make that into an abstraction that we can add into the library.
[00:05:27] So asynquence ships with about 10 or 15 core methods that are the base ones that you need. And there's also an optional contrib library with about 15 to 20 other optional step methods that you can add in for all this other higher level stuff. We'll get into reactive programming and CSP and other things later in the workshop.
[00:05:51] One other thing to point out, as you see on line 12, I have a .seq step type. What's that all about? Well in the regular Promise library, the then functions is polymorphic. That's a fancy way of saying it changes its behavior based upon what you return, what you do with it.
[00:06:09] I tend to think that polymorphic functions are a bit more confusing. So when I look at a then function, I don't actually know is it returning an immediate value, is it rejecting, is it returning a promise for something, I don't know what it's doing and I have to inspect it more closely.
[00:06:24] The design of asynquence said, rather than having one polymorphic function for everything, you name the step based upon what the step does. So the sequence step, the .seq step, is expecting a sequence back and a sequence is like a promise. So it's expecting a promise back basically, which is why we return on line 15 just like you do in a normal promise thing.
[00:06:49] If you do need to produce one externally and return it, you use a .seq step. If you don't need to produce your own, you use a .then step. If you need to do a synchronous step, like you just need to print something out and you don't need to do anything asynchronous, you use .val for the values.
[00:07:07] You inspect or transform a value. So basically, the design of this API is such that each method name does one specific thing, and its named based upon that expectation, rather than having to learn all the different polymorphic behaviors of one method.