Creating an observable from scratch (live-coding session) - Ben Lesh

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
I'm I'm I'm purely just winging this all right this isn't this isn't something I've prepared this is something I'm basically gonna try to live code and observable to you and try to explain what observables are from that angle a lot of people see a doodle and they're like oh there's some magic to this it's like it's not a promise promise there actually is magic to it there's some scheduling that goes on there's some state under the hood it executes things immediately in some cases and later in other cases like it's it's complex so people assign that same complexity with a familiar type like promises to observables and I'm here to tell you that that complexity does not exist there's complexity around how people use observables but not around the actual type itself so I'm gonna do this on stack which some of you may or may not be familiar with stack le'ts it's a really super cool tool that's basically Visual Studio code in the browser if you want to play with RCS you can actually come in here and start an app right here and it brings up rxjs I'll show you that really quick quick like some example code it's already imported the dependency that sort of thing this tool if you ever use it if you just were to use rxjs here and not import the dependency it gives you a prompt you click it and it you have the dependency really really fast is pretty cool but I'm going to do this and I'm gonna I'm gonna really punish myself and I'm gonna do it with typescript you're gonna see me casting things as any a lot just because I don't feel like terribly punishing myself but so let's go here and I'll bring up the console hopefully everybody can see this okay um I don't need any of this so what observables really really are are they're basically functions they're functions that you call subscribe in order to execute them you can implement your own observable pattern with nothing more than a function so I could have this here and I could call my observable and to my observable I could pass an observer which is a type that you look at the interface a server generic which means I have to make this generic this generic and the observer has a next method on it that takes a value does nothing it has an error method on it that takes an error of any could be any type of error and does nothing and it has a complete method on it which does returns nothing right so this in a nutshell is what the observer shaped like and then your observable if you wanted to emit values from it you're going to say server next one server next to observer next three and observer complete now in this case I am actually making all of this number so I can probably just go ahead and say this is an observer of number art code this in here so this is a verbal of the numbers one two and three and if I wanted to subscribe to this thing I would say my observable pass in an object with that's something with the value does something if there's an error and does something if it completes Lago not Lago alright so you can see down here and I'm going to just put at the top of this clear so when this runs that you get one two three done right away alright because I call the function with this ho gel object that has the expected interface and inside of there I call it an interface net values pretty cool this is basically what an observable is a lot of people don't know that this is simple so why why would I bother having a whole library around this if I can just build it with functions well there's a few problems with this for example I could say server next or and we're gonna get when did he done for well that wasn't really done that was a dirty lie that it was done it's not complete at all so now what do I do I I could augment my observer here to have something inside of it that says hey if something called complete on this then don't allow next to come to come through right but then I have to do that every single time I subscribe to my observable that's not great I could I could say well every time I call this observable I could say alright well maybe I'll just take this and I'll say observer make safe observer and I pass in there and I have this other method that takes the observer and wraps it in another observer that guarantees that sort of safety right but then every time I define an observable I have to call this makes a function right like that's probably not the most ideal solution and there's there's other other things of this that you start to see when you start to do things that involve asynchrony or any sort of like wanting to tear things down so if I wanted it's a little bit more complex than just one two three complete let me get back to that really quick maybe I want to do like an interval that's a that's a pretty basic idea so if I wanted to have an interval and I believe that this is going to I might have to I might have to refresh this repeatedly if it doesn't I think I could just hard reload here but we'll see um I'm concerned that if I start an interval it's gonna it's gonna go forever and be a problem but um so let's just say that if I wanted to make this like observe something acing or it's like an interval I would say okay well let I go one so we have some values we can pump out and then I'm gonna say Const ID equals set interval and every time that it's hit and I'm not gonna finish it so it doesn't start spamming the console right away I'm gonna do once every second and then I could return some sort of teardown because we want to be able to tear this down when we no longer care about it right this is just gonna increment I and then all observer next on it and here we're gonna it's gonna call clear is your goal with the ID so saying one two three and so on now my observable actually returns something I can use for teardown which I could call it like a subscription and or even just we'll call it teardown and we can say later on after a timeout give it three seconds or so most close enough they're tight though I'm gonna I'm gonna call teardown so we're going to wait for 3.2 seconds call teardown so this ticks along and then it's about there and then no more ferry down was called and you can see in here if I console.log this you know should be apparent there and turning down so I can even have like a cancellation mechanism like this but you get into even more problems where let's just say that I wanted this to go for so long and then complete like if I get in here and I say well this is this is a little different I want to say if I is greater than three and observer or dot complete when it's complete I don't want this interval to keep going now right now I'm responsible I'm on the hook for four not only do I have to make sure that my observers safe and doesn't want me to call next after after you've already called complete or error but I also have to like make sure that this whatever happens in here is called whenever you complete our air also right you have to make sure that the guarantee that these resources are torn down so this is why it's not a function but this is literally the only reason it's not a function everything else about observable is that is is it's just this sort of shape of function we have a function did you pass an observer into and then you return some sort of function that has like a teardown to it so if I'm going to implement re implement this from scratch to make it better what I want to do is I'm gonna have to create a new class called observable obviously and the constructor is literally going to take let's say this is a knit and it's take that same shape function that has an observer which is an observer he returns we'll call it teardown and teardown is just a type it just right so this is this is our constructor for it and then inside of this subscribe what we're really doing is we're taking an observer key and we're literally just going to say return this and it's observer just to start with just to make this do exactly the same thing that it's already currently doing and they're not now I can take the entire body of this and change this into bounce my observable equals you observe all this is now an arrow function I'm gonna close that up and this have to subscribe to now that's it so now we have an observable class that we've started with but it hasn't added the safety guarantees to it right I can still go in here and call next after complete and we're still gonna have that problem so we need to need to do something to make this safer or you know maybe I even want to have an API which rxjs does of where I don't you don't have to have all of these implemented on on the observer you can just provide next or just provide complete or something like that so what we do is we take that observer and we can have a class and I'm gonna here class we'll call it safe subscriber I was called subscriber subs generic type and so scriber is going to implement observer e that means we need to have a next on it oft you'd have an error on it any and we need to have a complete on it take nothing and what we're going to do is in the constructor we're gonna say that it has a hold a destination give it an observer and then inside of here we're just going to add little bits of state there's going to be this state that says [Music] see it closed and here we're if this stock closed or if it's not closed rather then this dot destination next value and then inside of here where there's an error complete we're gonna say if closed not if it's not closed O's equals true so now it's immediately closed synchronously right there and then we're going to forward that along and then the same basic idea here is gonna happen incomplete only we're gonna call beats you don't have an error anymore there so now I've at least out of the guarantee to where I can't go in here and I'm gonna make this tick a little bit faster so I don't have to wait so long where I can't go in here and say observer I haven't done it done it yet right now I can say observer next right afterwards and right and it's going to hide why do I have so many cuz it's what oh yeah yeah right all right so let's let's go here and say ha ha so it made it past there it's still going it's not it's not tearing down right when I call complete that's still a problem I have to solve and it's also next day after completion so let me let me make this a little bit bigger again oh boy say or actually I'll just make the other one smaller I've blocked up the browser only I have worked at a company that can make a really fast browser um please hold it's not good I'm gonna end up having to recode all this stuff yeah that's great we'll see if that comes back and we'll also see if we have all the code in here do you eat no we do not that is not good this right if I could I can't even I can't even right-click this that's how bad this is oh wait it came back yeah okay there we go doink that's all he wanted all right sir so back back to what I was saying so this is this is bad it doesn't like that it didn't actually build that I have to put a number out of here like something that certainly I wouldn't get to but it we've we defended against this successfully now wait is it doing that observer next it's calling complete oh because it tears down before it gets to call complete ha ha so funny so yeah so he gets it done and then it's it's still allowing this to continue on through here so that's a problem we don't want that and so it should never ever say 99 we should we should definitely stop it from doing that and we've got that now with our subscribers so we're gonna use our subscriber to wrap our observer which is obviously not safe our observer is just a plain dumb object with three functions on it that you can call in any order you like anytime you like so subscriber is going to add some sanity to that and what we're gonna do is we're just gonna take our observer inside of our subscribe which is up on our observable here and I'm gonna say subscriber goals new subscriber server then I'm going to subscriber through it so now I've solved that problem at the very least it no longer allows you to next or complete afterwards however if I was to make this teardown last a little longer it'll be more obvious that there's a problem there where basically it should have torn down right as synchronously as soon as it was done but that that interval keeps going right so it keeps going basically until I call teardown if I if I remove this here that interval is gonna eat memory and lasts forever I want that teardown to to happen at exactly the same time as I call complete so I need to tie this in some way to my subscriber the subscribers particularly the the error and the complete furthermore if if I call if I call a teardown on this I want to make sure that if I've implemented some sort of observable observable here that has some additional code that's running like you can't still next into that observer if if someone's already if someone likes subscribes and synchronously on subscribes like right afterwards you don't want to allow next to still be called inside the body of that thing and it's it's technically possible so what what we need to do is we need to tie it kind of both directions so first I'm going to deal with the making sure complete calls some sort of teardown and what we what we're going to do is I'm going to create a class called subscription and it's subscribe method on it and in this case I'm going to I could either add an ADD method is a ArcGIS does or I could even have it passed via the constructor in this case I'm just gonna have an ADD method and you're gonna add tear down to it which is teardown and internally it's gonna have what's called tear downs Bush teardown I need to add a private property private tear downs Bulls gray and what's going to happen is inside of here it's just gonna say alright for here down here down this basically then you can you can also you know so when you call on on this subscription it's gonna go through every teardown you added to the subscription and call it turn executing to tear down and what we'll do is we'll say it okay well this is now going to return a subscription gonna break up a whole bunch of stuff cuz I'm returning the wrong thing there and I'm gonna create a subscription right at the start subscription those new subscription subscription and then here I'm going to return the subscription and before that your subscription comes out of that so now I've got the that it's going to complain after it's it hits this because this is now a subscription so we have an unsubscribe method we've implemented basically the same thing only with a subscription but now I've got this type I can pass to subscriber to complain about because it doesn't take that yet and things going dim on me up here on need to change this you accept a private subscription and then closed can be derived from from a number of things but you know I could just cheat inside of here and say oh well whenever you create the subscription I want you to add something that says this stock closed true right just to kind of guard against if this if this is unsubscribed don't allow anything to next door or air and then down here right after I send the the air I'm gonna synchronously say this dot subscription subscribe both of these spots so this is this is effectively observable in a nutshell this is this is what our kiss is doing for you is it's it's taking a function that you passed as the body to your observable constructor and it's adding some safety guarantees that's that's all the type is doing it's not magic it doesn't have scheduling it doesn't have any state like a promise it's not immediately executing anything it's wrapping a function in something of a particular shape and from that what we have is now we have this thing that I talked about before where we've got you know it's a set of values when I do it my water set it somewhere weird anyway we have a we have a set of values over time and then we get into what is really exciting about our chests which is operators and so what operators are is they're simply these things that transform one observable which is this simple thing into a new observable which is a different simple thing which is they're just functions you're transforming functions into function so you're taking a function you're applying operators to it and building a new function is what you're really doing so when you subscribe to it it executes whatever function you've built which is composition of a whole bunch of functions under the hood now the way we do this in our X is a little bit different than in the past where you had dot chained operator so dot chained operator would look something like oh in my observable I'm gonna have map and I'm gonna say add map you give this function I'm lazy I'm gonna put any there and then what I'm gonna do is I'm gonna return a new observable and that new observable gives me a subscriber actually it's a little better it's a value of T thank you very much which goes to R so this is this is my my member mattchambers of this and this is going to be observable our expects me to return I'm sort of teardown and it's inside of here what I'm going to do is I'm going to subscribe to the source which is this and I subscribe with a brand new observer and that observers whole job is going to say hey pass this on to the subscriber but only after I apply this function to the value and then the rest of it is going to be error it's gonna be passed through so error subscriber the next one is the same thing only with eat doesn't care about that this so this is this is what a map function looks like and so now I could go down here and I go I've got my observable I'm subscribing to it and I want to transform it in some way so like right here I'm gonna say map let's add a hundred to it right so I've added a hundred to everyone but this is all an operator is is is this this function that takes an observable and returns a new observable that subscribes to that source observable with some custom subscriber that this observer here is forwarding on to this observer here so your so when you subscribe you're kind of chaining observers together right and so the more maps I put in there the bigger this chain of next next next next next becomes that's that's what it looks like when it's dot chain so this is where we came from before and the problem with this is as I stated there's there's so many things you can do with a set of values over time so many different operations you can perform that now if I were to provide all them to you you have this exhaustive list of like 60-plus things which by the way I inherited came from a compiled language like C sharp where the size and the number of these things doesn't matter because it gets compiled away at Build time and your I've got all these things in our prototype then I have to ship it to the browser and you've got you know of to 200k of of stuff that you didn't really want shipping with your code so if the first pass was and you saw this in rx gs5 was like oh well we can do this thing where when you import a particular file it automatically augments the prototype there's problems with that if I have a library that uses map now everybody's observable like has map on it and they start depending on that and then I decided I don't need map I remove it and I break everybody downstream there's no way for me to know how I did that because I was mutating global scope in that prototype an observable dot prototype so that's one that's one problem there's other various problems around dot chaining it's not it's not as easy to break apart into various sections of things so the solution of this and the other thing is you can't free shake away something that's been added to a prototype at least to my knowledge unless bundlers have improved that since then I don't think you can no thanks so that's that's another problem if you have 60 plus operators the only reason two of them too bad or you've added one with the other mechanism and you stopped using it you're still adding it to your bundle so a better way to do this was to be it was like well what if I implement this as its own standalone function which involves me just taking this entire thing here and removing it and then saying okay well I'm SMAP this is gonna have the blocking - so this is this is same sort of thing but now I'm gonna have this higher order function that takes a source observable right and so instead of this being a source or this the source is actually coming from this higher order and you can implement every single operator in terms of hey I'm a function that returns a function of this exact shape which is takes an observable and returns a new observable and now I need a way to kind of apply this and so the rudimentary way it was there used to be an operator called let's which is a horrible name as you can see from the color coding right here so we'll call it something else like let's write and you could provide to this basically any function that had a source observable source of T so it had to match the same type and returned some turn some observable of our R type scrip is a good choice for this and I do love typescript actually but so then inside of here what this has got to do is it's got to say okay well whenever you subscribe to the result of this which is a new observable of our turn new observable R it is subscriber we call it s cuz I'm super lazy and then this returns you know what I picked my mapping function down here I just remembered ah-ha so I subscribed and I never unsubscribe who writes this library this is not a return sorry my bad don't use this library that I'm writing right now um so this requires that I'll return to tear down because that's the contract of this particular implementation inside of here I'm going to basically just say function I'm going to pass this to it and then I'm gonna subscribe with the this subscriber which has the same shape as an observer and then inside of here I need to capture the subscription again this is what let looks like and that enables me to say or elect looks like that and that enables me to try to use map in a different way where I can say let's app and we get the same exact result but it sucks to have to be do dot let map dot let map dot let Matt write like why why would you want to do that so there's a there's another way to go about this and it's a bit of functional programming and there is there's a thing and it exists in like Ram DES and some other things called height and I'm gonna make pipe right up here it's very very small it's a pipe and pipe takes a rest of arguments of functions and these functions in this particular case are always going to be of this shape or it's a source of observable in this case any typing this is actually hard to observable of any need to define this below because yet are we oh my god rolling and that's something up what's it complaining about oh it's not in a race height rate of those all right and what we're gonna do with this is I'm just gonna return something that takes the source observable of any and then takes that entire array of functions and reduces it such that we take the previous and whatever this function is and we've it call a phone at that function on the previous value and we start with the source missing that there so this is all this is all pipe is is it's just like hey give me an array of functions and then I want you to apply the result of this one to like pass it to the next one and take the result of that one and pass to the next one and so on and it's a bit of functional programming magic you never really have to implement this it exists in RAM that exists in arcs yes is a utility helper but we're gonna take this and instead of implementing this horrifying let's just method I'm gonna call this pipe and this is going to take the same basic argument here which I'm going to turn these to any because otherwise we get into type hell in this particular example you can go look at the source code for arcs just to see how that has to get typed but rest arguments are hard it's the best way to put it and then here I've got this I'm gonna return this new miserable yadda yadda we don't even need to do that anymore we can just take this and say return pipe pass all the functions to it and then pass this to that and it's exactly the same thing as I was doing with let believe it or not only we're allowing more arguments so in fact I step and let I probably did not need now that I think about it but now I can say all right here's pipe and I can do math and then I can map it again get more exciting right this is this is the entirety of our chess as written up here it really in a hacky way and what 20 minutes or something like that every everything you really need to know about what's going on with our X's right is this the thing that blows your mind that people can't wrap their heads around sometimes is the fact that since observables are just functions you can do anything with an observable that a function can do right and so people do anything have been observable that a function can do like a like you for example sometimes you have an observable that when you subscribe to it it creates a new web socket and then like which creates a WebSocket connection and then next stop values sometimes you have an observable that much like a function it the body of it literally closes over an existing web socket now you're sharing it right so there's there's and that's the difference between a cold observable and a hot observable don't use that terminology it doesn't make any sense what happens if you have if you create a web socket in your absorb what you also close everyone now is it a lukewarm observable I don't know sounds great but that's that's the general this is the general basis of upon which our X is built and it's true in pretty much every language that our X are some version of our X exists in which it's true or swift I think there's an objective-c version there's go exists somewhere it's not there's one in the official or reactive X but I don't know rx PHP exists rx C++ for masochists CPP there's there's you name it it exists in most languages and all carries the same sort of vernacular so I welcome you I'll save this and I'll send the link out you can play around with it don't use this I can tell you firsthand that there are bugs here and there's a lot of testing to be done around building your own operators but if you wanted to build your own operators and have them work and rx like this here would literally work and in our X would be just fine or my this this observable would work in our X this map method all of this stuff would work in our exits it's all the same thing so with that I'll open it up to questions so there's there's two ways to as you see here what would tear down occurs in three different ways and tear down is what you're really after when you unsubscribe but for the most part and it's just a free up resources so tear down occurs when the consumer unsubscribes or it can occur when the producer of data says I'm complete or when the producer of data says I've got an error and you can force completion with take operators so if you say take three the implementation of that operator has a counter inside of there and says hey I've gotten two three I'm calling complete and then teardown occurs now take until is more useful for the situation's we're talking about so take until you can say take until destroy happens and you can do this sprinkle it out throughout all of your arcs that you're doing and what what you can do is have like a subject that you next into you don't have to complete it you just have the next on it in your on destroy and that can that can like reactively kill all of the other observable that you've got going so the advantages to this are now you've got this composable thing that you can build from any other observable that you want to the disadvantage is now you've kind of you have to make sure that somewhere you've got that taken till like you've kind of moved the problem a little bit another thing you can do is you can create one subscription that's like the parent subscription for your classes we're just what I actually most commonly do and then you take all of your other subscriptions and add it to that so in rx you've got the subscription dot add takes subscriptions or tear downs and you can add a whole bunch of subscriptions to a parent and then when you call unsubscribe on that it unsubscribes everything below it so there's there's a different choices none of them are right I tend to go for the add everything system parents of subscription unless I have some sort of special need like well now I'm in a situation where not only do I want to kill this observable if if the component unmount s-- but i want to I want to have a button that can kill it all so now I've got two ways to kill this I should probably use taken too sure so the question for people streaming is now that I'm on the angular team do I have any ideas about how basically ArcGIS could be better integrated or the the user experience could be better for people when it comes to Rx and angular I do think so we have this really nice root user experience around subscribing which is good we don't have the best user experience around getting observables out of your template and I think it's a sore point and in particular when it comes to certain things that can happen around server-side rendering or other things like that so the idea is and there's there's actually some design Doc's around that this is to add some add some syntax to the templates that allows you to say this property on this component is an observable and like this this button or whatever in the template is next thing into this observable and you get a cold observable instead of having like a subject of that when you subscribe to it does the work of of binding to the events or whatever for the for the the actual elements on the page so that's that's one side of it so like making it easier to create observables from user events is one another one that I'd really like to dig into is trying to provide some kind of blessed organ ah mix around the HTTP client as far as because it I think is a really common use case people want a multicast there their HTTP results but not always and you don't want to force that always on them and you don't want to you know remove cancellation on them but you want it you want to make sure that you can cover the 80% use case for people in such a way that you know they don't maybe they don't always have to use share or maybe they don't always have to use concatenate or whatever like just to try to try to make their lives a little easier but that's that still something needs a lot of exploration but those are areas that I would only look at so that way if someone wants to use angular like you know I would like it if the RX GS stuff didn't get in their way I think a lot of people really like it and really enjoy it but there's some people that don't so that's that's kind of the where I would I would personally I think there's some architectural gotchas there a little bit because for a while there to repeat the question are there any gotchas around angular change detection and ends like emissions from observables presumably subscribe to pi basic right so obviously if you subscribe to an observable and you're just setting state on your component then that's gonna go through regular change detection just like any other code you might write does in the case of pipe async for a while and I don't know if this has changed I sincerely hope it does but in the case of pipe pipe async there's this interesting thing that angular does where it's like oh well I see you've subscribed to this observable when it emits I'm gonna run change detection and in reality if you if with that pipe async you know what you're binding to and you know you have an event you probably could just set that directly and not have to go through the rigmarole of change detection so I think some people that know rx well that that are new to angular assume that the what I'm what i just said is probably what's happening and I don't really think that's the case right now I know it wasn't in the past it may have changed out from under me there's a lot of people working on the code base and I haven't looked at that in a while but so don't take it as gospel people online but there's there's some weird hoops that get jumped through I think in PI pacing that probably don't need to be jumped through at least from my understanding of it but that'll be that'll be cleaned up fairly shortly
Info
Channel: AngularNYC
Views: 7,753
Rating: 5 out of 5
Keywords: Ben Lesh, RxJS, Live Coding, Observable
Id: m40cF91F8_A
Channel Id: undefined
Length: 43min 42sec (2622 seconds)
Published: Fri Dec 07 2018
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.