Isolates and multithreading in Flutter (The Boring Flutter Development Show, Ep. 30)

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments

It was very boring, I loved it, thank you!

πŸ‘οΈŽ︎ 11 πŸ‘€οΈŽ︎ u/lecksfrawen πŸ“…οΈŽ︎ Sep 27 2019 πŸ—«︎ replies

So you went 20% over time and decided to speed up the video by 20% to fix it? ❀️

πŸ‘οΈŽ︎ 2 πŸ‘€οΈŽ︎ u/bobbyQuick πŸ“…οΈŽ︎ Sep 28 2019 πŸ—«︎ replies
Captions
[TONE] [MUSIC PLAYING] FILIP HRACEK: Hello, everyone. We have an experiment for you today. ANDREW BROGDON: Yeah. We have sped up the following episode by 20%. FILIP HRACEK: Exactly because we realized we are a boring show, but we can be a little bit faster at that. ANDREW BROGDON: More efficiently boring, basically. FILIP HRACEK: Yes. Yeah, so please give us feedback. Is it too fast? Is it too slow? ANDREW BROGDON: Yeah, not fast-- we could go-- we could faster. FILIP HRACEK: We could go very, very fast. ANDREW BROGDON: Yeah, but it's an experiment. So please leave a comment below if you have strong feelings one way or the other, and let us know what you think. [MUSIC PLAYING] FILIP HRACEK: Hello, everyone. My name is Filip and this is Andrew. ANDREW BROGDON: Hello. FILIP HRACEK: Hi, Andrew. And you are, again, watching the "Boring Show." So today, we are going to look into isolates. ANDREW BROGDON: Yes. FILIP HRACEK: And that's a very exciting topic. And this is because why would we want to use isolates? Because of performance, right? Not that we see any problem with the performance of our app, I don't think yet, but still, it's nice to not do things on the main thread. And this episode, like, let's just start going, right? A little introduction-- what we do here, this is the Hacker News app. If you haven't seen it, it's just a reader for Hacker News. It has top stories, new stories. We have some mechanism called HackerNewsTab and HackerNewsNotifier that tells us or that lets us just basically talk to the API of Hacker News. And when I say talk, I mean listen. We don't say anything to it. And basically, what we are doing is we get the articles list from them, and then we parse it, and then we show it here. And that's it, right? And in the last few episodes, we did things like navigation, and favorites, and all that. And we'll get back to that, but I think it's a nice idea to just go to the performance stuff. ANDREW BROGDON: Mm-hmm. Yeah, and so I think we get a lot of questions about isolates and multithreading in Dart, mainly because I think people think they need to know it. And I think that one of the interesting things is you can do almost everything you want to do with Flutter in Dart without ever creating an isolate. Like Dart, it uses an event loop. It does asynchronous stuff very, very naturally. And you don't always need to go to the trouble of spinning up another isolate to do something. One of the ways it can help us in this app though is if we do download a whole bunch of articles in one call, and then need to parse them out of JSON and into an in-memory representation, we could do that off the main thread, so to speak, with an isolate and make sure that if our app needs to rebuild, you know, if it's doing an animation or something like that, it's able to render frames and it's not going to be blocked. FILIP HRACEK: Right. So again, we don't see anything terrible in terms of performance of this particular app, but it's a best practice probably to do your non-UI-related workload out of the main UI thread. And here, we're actually doing a lot. Like, if we switch to the screen, you can see that we have parse stories here-- parseTopStories-- which is basically-- well, getIds, I guess, gets all the-- like it fetches from HTTP the whole thing, which is like a huge JSON file, and then it parses that into a string, and then it parses that string-- that huge string-- into like math representation. And then it takes the first 10 of those. So we do all that work. And then, we're, like, oh, no, no. We just want the first 10. But there's no other way to do it, right? If you want to parse a json, you probably want to first get the whole json. And you have to parse it, the whole thing. And then, yeah-- from there, we go and parse the subparts of that big list into articles. And then we show them, right? So we have this article class. And that is basically all we want to show to the user. And we have what I was just showing, which is the get IDs, and then get articles from each. That's a lot of work that we're doing on the main thread asynchronously. So it's not a huge deal. OK. Let's do the easiest way to do any kind of multithreading in Dart. And that is the compute method. Maybe let's talk about multithreading. ANDREW BROGDON: Yeah. So we actually just put out a series on asynchronous stuff in Dart. One of things we mentioned in the very, very first one was how isolates are constructed, what an isolate is. But to sort of give the short version, the way in Dart-- all Dart code is executed in an isolate, which is just a little space in memory on the machine. It's a little section of memory that's blocked off for a particular isolate. And it's one thread running an event loop. And so if you're running Dart code, and you have stuff that's, like, waiting on button tabs, or events from html, if you're using Dart html or things like that, there's a little event loop that processes those. And it's allowed to modify this block of memory that's in this isolate. So you have this little box with memory and a thread. And then if you want to do another thread, if you want to do something so big that it's going to be very busy for a long time, you can create another one of those little isolates, another one of those little boxes that has its own thread, and its own event loop, and its own block of memory. And there's no shared memory between the two. That's one of things that you're going to see when we get into this. In order to have data passing back and forth between the two, they can't just have pointers to the same piece of memory. They actually have to make a message, pass it from one isolate to another, and copy the content of that message into the memory of the other isolate, the one that receives it. So it's the source of the name isolate. These little spaces are kept isolated from one another and can only communicate via passing messages. FILIP HRACEK: And this is how Dart tries to kind of sidestep the issue of multithreading in many other languages, where it just gets really gnarly to synchronize threads and stuff like this. Here, it's just, like, oh, you have an isolate. All you can do is just post messages between the two. That said, we have a helper method in Flutter called compute which does most of the heavy lifting for you. And let's read docs, all that isolate around the callback. OK, maybe I don't want to read it here. ANDREW BROGDON: I assume this is going to get down into the runtime pretty quick, as you go. FILIP HRACEK: Compute-- wait. I'm looking at [INAUDIBLE]. So that would be flutter.dev and docs, and API docs. ANDREW BROGDON: You can probably just use Search. FILIP HRACEK: Hm. ANDREW BROGDON: I will compute such a generic term, it might return, like, the phone book. FILIP HRACEK: No. I think you're right, yes. ANDREW BROGDON: There we go. FILIP HRACEK: Compute property. Spot-- wow. I thought I would get a, like, you know, a proper-- ANDREW BROGDON: Is this the same comment you had before? FILIP HRACEK: Yeah. ANDREW BROGDON: I mean, the bottom line, compute-- it spins up an isolate for you. It takes a function. So it spins up an isolate. It gives that function to the isolate and says, hey, run this. And at the same time, it gives it a parameter to say, and here's some data to start you off. And then it goes off and it runs that function on the other isolate. And then whatever return value comes out of that function gets passed back as a message and pops out as the return value of compute. FILIP HRACEK: Right. Yes. So that was what I was looking for as the signature, which is like, now, like, through typedef. Basically, you'd give it a callback. So let's give it a pull back. And you have to use a callback that's not a method, I think? Or is it-- ANDREW BROGDON: It returns a future. So that's how you get the data out of it. You're giving it a method that-- FILIP HRACEK: It cannot be-- it has to be a static method. It cannot be a method on a object, because-- right? ANDREW BROGDON: So it wouldn't have an object [INAUDIBLE].. We should try it and see what happens. What's parseTopStories? FILIP HRACEK: Oh. It is a static method. So parseTopStories-- we just give it the [INAUDIBLE] just-- let's just do a final result, or var. I see a lot of vars here. And then-- ANDREW BROGDON: So it needs the function. And it needs the data to give to the parseTopStories. FILIP HRACEK: Response.body-- what is going on? Analysis. Couldn't infer type parameter 'Q'. OK. ANDREW BROGDON: So compute has two type parameters. There's q, which is the type of what you're going to give it as its parameter. And then there's r, which is the type that it's going to return when it's done. FILIP HRACEK: So string versus-- ah, what's that do? I think it's a list of articles? ANDREW BROGDON: Mhm. FILIP HRACEK: No list of int-- oh, we're starting with-- ANDREW BROGDON: Just the IDs, right? FILIP HRACEK: Yes, yes. So we have this. And then we still just take 10? OK. ANDREW BROGDON: Do you want to put a dent on it? Because that result is going to be a future. FILIP HRACEK: Or-- and now, result-- I think this is a list. Oh, yeah. We're taking 10, so which is not the list. And then we have to go to toList then. OK. All right. So let's see if this actually works. So-- ANDREW BROGDON: If this works on the first try, I'm going to high five you. Just letting you know it's coming. [LAUGHTER] FILIP HRACEK: All right. Let's see. Should I just-- ANDREW BROGDON: Do you want to-- do a full hot restart just to be sure. FILIP HRACEK: I want that high five. ANDREW BROGDON: What do we got? Did that? FILIP HRACEK: Yep. [HIGH FIVE SMACK] ANDREW BROGDON: Nice job. FILIP HRACEK: All right. So I think-- ANDREW BROGDON: And we got nothing on the console, right? No, like, hey, I'm giving you old data because that didn't work. Huh? All right. FILIP HRACEK: So get IDs. I still don't understand. Maybe we just have an old-- but why does it say parseTopStories? Because this should be parsing all stories, right? ANDREW BROGDON: I think there's two-- it's been a while since I was on the [INAUDIBLE].. As you can tell, I'm working on the hacker news app. Aren't there two calls that we do? There's one to get the ID. And then another series of calls to sort of hydrate them? FILIP HRACEK: I think it just says parse. It should say parse stories IDs or something like that. You know? It's not top stories. ANDREW BROGDON: [INAUDIBLE] was article. FILIP HRACEK: Yes. ANDREW BROGDON: Yeah. FILIP HRACEK: So parseStoryIDs. ANDREW BROGDON: I would-- we should look at the hacker news app before we change it. Just because top stories may be the particular call. FILIP HRACEK: I almost-- you know, we had-- ANDREW BROGDON: Like I just said, I haven't touched this app in several months. So I shouldn't be, like, mm. FILIP HRACEK: No, I'm pretty sure we had like parseTopStories and then we basically copy-pasted two methods. And then only one survived. And it still has this legacy. So I think we work on with this. ANDREW BROGDON: [INAUDIBLE]. FILIP HRACEK: Thank you. And it means a lot. [LAUGHTER] Update articles is what? What is this? Oh, this is just something. What we want is actually getArticles, which calls parseArticles. It does zero error thing, like, we should at least have a try catch, right, for this, maybe. ANDREW BROGDON: Because this does come up occasionally. FILIP HRACEK: Yes. ANDREW BROGDON: That's come up in other episodes, where top stories gives you a list of 10 article IDs. And then, it turns out that number eight is no longer an article. FILIP HRACEK: Yeah. Yeah. ANDREW BROGDON: And so it fails. FILIP HRACEK: Yeah. But we can't really put a try catch around a compute. Because compute will just create a new isolate. It might actually send us some errors, not sure. ANDREW BROGDON: That's a good question. I mean, it returns a future. So the future could certainly complete with an error. But whether the implementation of compute is smart enough to run things in a zone and catch an unhandled exception and report it properly-- FILIP HRACEK: Let's try it. Oh, yeah. All right. ANDREW BROGDON: You're just going to write spin up compute. And be, like, here's a function that just throws an error. It's just what happens. FILIP HRACEK: Yeah. I'm just going to give it a false URL. And it's going to fail trying to pass a URL that's not really-- ANDREW BROGDON: OK. FILIP HRACEK: OK. OK. So first of all, we're basically-- we do get this. We're not even checking if this is-- we're doing nothing, basically, here. Saying, like, oh, yeah. This is what happens here, basically. Right? And I think we should just throw, maybe. ANDREW BROGDON: This is good. Oh, I thought you were going to do this in the column that we just did with compute. FILIP HRACEK: That's also the next part. But I'm, like, if we don't even get the http request back, I think we should do something and not just ignore it, right? Should we throw? ANDREW BROGDON: Well, I mean, right now it's throwing. Right? Because it would-- FILIP HRACEK: Well, this one, if we-- ANDREW BROGDON: If it turns it to a 404 or something like that, you're right. FILIP HRACEK: So what would kind of throw? I just default to StateError, even though it's [INAUDIBLE].. And-- ANDREW BROGDON: There's our state exception. So we can get into errors versus exceptions. Because this would be an exception rather than error, right? It's a recoverable. This is something that came up around the shop, recently, the difference between errors and exceptions. And I think we just go-- like, in Dart land, there is a standard for those two terms. An exception is a bad state, but one that's expected to happen occasionally and is not the fault of the program, the app. Like this you expect every now and then. You might get a 404 or a 500 from a web server that's not working properly or a bad ID or something. And so you're expected to handle that and move on. And so that's an exception. Whereas an error would be, like, you just asked me to divide by 0. What's wrong with you kind of a thing. FILIP HRACEK: Right. ANDREW BROGDON: That's the fault of the app itself. And so that would count as an error. What are we doing? FILIP HRACEK: I'm trying to do the right thing here and say, huh? Oh, I don't need to? ANDREW BROGDON: Well. There's a-- FILIP HRACEK: The [INAUDIBLE] constructor-- OK. ANDREW BROGDON: Wait. It was a factor method, the factor constructor? FILIP HRACEK: Yeah. I think what it wants is a message like this. Why? ANDREW BROGDON: Go look at the constructor there. I think it said something about a factory method. FILIP HRACEK: You mean this one? ANDREW BROGDON: Yeah. That's a factory constructor rather than a typical one. FILIP HRACEK: Right. But it's just, like, it-- OK, how does everyone else do it? ANDREW BROGDON: When in doubt, copy from others. FILIP HRACEK: Right. Oh, you just-- see. You're doing the right thing. But also, I'm getting a little-- regenerative constructor exception. That is-- ANDREW BROGDON: This is because I don't use, because we don't use factor constructors all that often. FILIP HRACEK: Yeah. But also, I thought it's much easier to construct your own exception. And I'm getting a little-- as the implements. You know what? Let's see. Like, a normal exception would be what, http exception? Or do they do-- yeah-- oh, it's implementing. OK. ANDREW BROGDON: Implementing the interface, as opposed to extending it. OK. OK. That's something that doesn't come up. We should mention that real quick. So there's two ways that you can extend something. So if you have an abstract class, every class in Dart defines an interface as well as a class with its public methods. And so you can either extend that class and get access to all of its stuff as a superclass, like, properties and things like that. Or you can implement it, in which case you're just saying I'm going to fill out the same methods in my class. And so you can use it as if it were that class. Right? FILIP HRACEK: Mhm. ANDREW BROGDON: And so exception with it's-- I mean, it's just a marker, almost, I think. FILIP HRACEK: Yeah. ANDREW BROGDON: It has a message in it. And that's about it. FILIP HRACEK: You can-- in Dart, you can throw anything through [INAUDIBLE].. You could be like, oh, let's throw a string. ANDREW BROGDON: Just throw 4. Anything but null, I think you're allowed to throw. You can't throw null. At least, I don't think so. FILIP HRACEK: Well, aesthetically you could, maybe. Anyway-- ANDREW BROGDON: I swear I looked that up once. FILIP HRACEK: I'm just going to do [INAUDIBLE] API exception. ANDREW BROGDON: Fun fact, Googling dart throw, not useful at all for coding. Got a lot of videos on how to throw darts, but nothing on my actual needs. FILIP HRACEK: Sorry. Is that a message for this? I know, I'm just going to shoot here and not do the message or make it optional. Now we're actually throwing something when there's a status quo that is non 200 or if the body is null, which at least makes me a little happier that we fail fast. Even though, we should probably catch the exception and then show something to the user [INAUDIBLE].. Back to isolates. We have this, and we already know how to deal with it. We have final result is waiting for a compute call. That compute call gets parse article the-- what do you call it? Tariff, right? It's basically just the function. Actually, this might be confusing if you're not familiar with just passing around functions. But we could just do something like this, right? Data and do something here. And that is the exact same thing. You're basically giving it a function. Parse article, nope. And then the data is OK in the story res body. And we're waiting. And then we're basically saying-- what does this get us? ANDREW BROGDON: You should just do underscore cached articles ID equals the way. FILIP HRACEK: Yes, I was going to put a try catch. So just result, right? And now try catch. You know what, maybe you're right. I should just try catch everything here. ANDREW BROGDON: Well, if you don't put a try catch on there, it's going to throw a Hacker News API exception if the computer fails. FILIP HRACEK: The Hacker News, this one only happens if the actual HTP get thing gives us a different than [INAUDIBLE]. This is trying to catch the parse errors. ANDREW BROGDON: Right, that's what I was saying, if we don't put another try catch statement in there. FILIP HRACEK: OK, sorry. So you were right. ANDREW BROGDON: Never mind, I'm confused. FILIP HRACEK: Like this, and then catch. What is this going to be? What kind of error should-- ANDREW BROGDON: I'm curious what type it is. You can print the runtime type, just so we know what it is. FILIP HRACEK: Print E, also rethrow. Once again, if we get the status quo right and we have a body, we will hopefully spin up a new isolate and that parses the article. At this point, we're kind of abusing, I think, compute because we get-- ANDREW BROGDON: This is going to be slow, which is an interesting point that we should make, actually. So let's just see what happens. FILIP HRACEK: So I'm going to just pretend that this should work. Ooh, wow. ANDREW BROGDON: What just happened? FILIP HRACEK: I don't know. ANDREW BROGDON: Let's open up-- there we go. Let's see what happened there. FILIP HRACEK: I feel like we should probably report this. This is definitely not something that we should go ahead with. I'm going to full restart. ANDREW BROGDON: Do one-- FILIP HRACEK: Yes? ANDREW BROGDON: Try it again and see if it crashes again, but then I have something I'd like to try. FILIP HRACEK: OK. ANDREW BROGDON: I'm spoiled. I hate full app restarts in rebuild. FILIP HRACEK: Oh, OK. ANDREW BROGDON: That's interesting. It was a weird hot restart issue? FILIP HRACEK: Yes, it was a hot reload. I was just feeling lucky. So this sounds like it does seem a little slow. And that's because basically we get the IDs. And then we are-- now it's not to be slow because we're actually caching the articles, like the cool people we are. ANDREW BROGDON: Are those just cached in memory? FILIP HRACEK: Yes. ANDREW BROGDON: They're not written down anywhere? FILIP HRACEK: Yes, which is good for us, because now we can try things. ANDREW BROGDON: We should talk about the downside of doing it this way. The compute is really, really cool if you're doing one little thing and you're not going to do it that often. Not even a little thing, but one particular thing. You're not going to be doing that same thing 20 times at once or anything like that, because it's very-- the code is dead simple too. You're spinning up an isolate and passing this message to it and getting the result. And it's one line of code for you to call compute, because it does all this stuff on your behalf. The downside of it is that when you call compute, it spins up a whole isolate for you, runs this one function, and then tears the isolate down because you're done with it at that point. If we went from take 10 to take 50 or something like that on that list of articles, we'd be spinning up 50 isolates at once to run these things off the main isolate, and then tearing all 50 down. And then the next time it reloaded, it'd be doing the same thing. FILIP HRACEK: Actually, I think with the way we were doing it-- let me see. Do we actually do it in parallel? I don't think so. We're iterating. So we spin up an isolate. We do the processing. We tear it down. We spin up another one. Oh, no. ANDREW BROGDON: No we're waiting the compute [INAUDIBLE].. FILIP HRACEK: OK, yeah, cool. ANDREW BROGDON: It's still one, then tear it down, and two and tear it down. And so you're still spinning up all these isolates that you don't necessarily need. FILIP HRACEK: It's cool that we can do that. ANDREW BROGDON: Yes, it is pretty cool. But a more efficient way to do this would be to actually make something to manage that isolate, and to have a long running isolate that can handle repeated requests for articles. FILIP HRACEK: Which I think we'll do. But first, let me try and crash the compute. If we do items, and now we do this. ANDREW BROGDON: Isn't that going to-- it's going to get down here. [INAUDIBLE] I would do-- Instead of the body, just give it a garbage string there. That would probably do it. FILIP HRACEK: Yes, or-- ANDREW BROGDON: Get rid of the items there. FILIP HRACEK: Oh, yes. You know what? Let's do it again. It wants to restart. We have to restart everything. I was like, stop doing things. ANDREW BROGDON: It's reminding me I got to release Veggie Seasons today. One of our iOS samples, Veggie Seasons, we need to push a new version of it to the app store. It's an actual app. It just happens to be open source. But let me push a new version. FILIP HRACEK: Wow, that's interesting. I think it's just going to do 20 of these. ANDREW BROGDON: So are we stopped? Is it broken on the exception? FILIP HRACEK: It looks like, yes. ANDREW BROGDON: Or is that the old one that we got before? FILIP HRACEK: No, no this is a new one. We are stopped in one of the isolates. It takes its time to kind of collect all the data of what's happening. I was kind of assuming that it would be completely transparent to us, and the isolate would not show up in the ID in the debugger, but it does, which is cool. ANDREW BROGDON: Can we keep running and see what happens? Sounds like your laptop is taking off. FILIP HRACEK: Yes. ANDREW BROGDON: Do you want to just kill this and start out doing it the right way? FILIP HRACEK: Yes. Let's do it with an actualize isolate. So how does one make isolates? ANDREW BROGDON: you use the spawn method-- isolate dot spawn. FILIP HRACEK: Probably when we are creating the hacker news tab. ANDREW BROGDON: [INAUDIBLE] FILIP HRACEK: Oh yes, sorry. When we're creating the hacker news tab or [INAUDIBLE] tab-- so that would be one isolate per tab. Is that OK? ANDREW BROGDON: What is the isolate going to do? Are we going to have it parse the story IDs or are we going to have it parse articles, or both? FILIP HRACEK: Yes, you're right. You have a much better idea, because I was thinking at the start of a tab, let's start an isolate and use it as a util isolate, which would always be running, but mostly just waiting for messages. But I think it's much better to be like, now we are starting something. Something's starting to happen. Let's spin up the isolate and then let it do the work, and then immediately after that, kill it. Sounds good? ANDREW BROGDON: So we kill it after-- FILIP HRACEK: After we get all the articles. ANDREW BROGDON: And then what happens if they hit refresh again? FILIP HRACEK: We spin it again. Let's see what happen. Basically here, refresh, because I think we're calling refresh even at the very start of all this. So we would be like, here spin up the isolate. Then do this on the isolate somehow, and then destroy the isolate. ANDREW BROGDON: OK. FILIP HRACEK: So isolate spawn, at least I think. Spawn, is it just spawn? And that's the function, which definitely needs to be either top level function or a static function. ANDREW BROGDON: Yes, not an instance method. FILIP HRACEK: What would we call it? ANDREW BROGDON: Isolate entry points. Call it, you know. FILIP HRACEK: Yep. So would we click [INAUDIBLE]? ANDREW BROGDON: Well, this is just a function name, right? FILIP HRACEK: Yes. Isolate entry. And again, this is just the function itself. And then this is probably the first message that we send it, is it? ANDREW BROGDON: Yes. This is our one chance. If you want it to be able to communicate with you, you need to send along with that message a way for it to communicate with you. And so isolates do that with these things called send ports, usually. FILIP HRACEK: Right, so we want to do-- Maybe we do something like a receive port. I always forget. ANDREW BROGDON: We would make a send port. And we would give that to the isolate. And then we'd listen to the receive receive port that's part of the send port. I believe that's how that works. FILIP HRACEK: Usually the initial message contains a send port, so that a spawner and spawnee can communicate with each other. Good idea. And send port is basically-- I think you can just do send port. It is send port. Is it? ANDREW BROGDON: We'd make the receive port. We make a receive port, and the receive port has a send port. FILIP HRACEK: Oh, we make the receive port. ANDREW BROGDON: So you could say final receive port equals new receive port. And then that message could be receive port dot send port. FILIP HRACEK: Yes. Do we want to unwrap this? We have two isolates and we need them to communicate to each other through something we call ports. There is send port and receive port. The receive port is what I'm listening to for messages coming from outside from the other isolate. And send port is what I use to send messages to that isolate. Here, we are in the main isolate. And I have a receive port, which means I can receive messages from you. And what I'm going to do is I will give you not the receive port, but I will give you the other end of the hose, basically. Take that and put things into it. ANDREW BROGDON: It works very much like a stream. FILIP HRACEK: Right. There errors are fatal on exit on error debug name. I don't think we need to do this. And what does spawn give us? ANDREW BROGDON: It returns an isolate object or future isolate object because it's possible for it not to work. If you're running out of memory on your device, it can be like, I don't have enough memory to spin up a isolate. Sorry. And it returns a future of an isolate because it takes a second for it to go spin up this isolate. And then it comes back and-- FILIP HRACEK: So let's make sure that we kill the isolate. Whoa. ANDREW BROGDON: There's no dash 9 for fun. FILIP HRACEK: No, it's void. It's just like, puff. And then we need to create the methods. And it's not going to be-- should do it a static method? ANDREW BROGDON: Yes, it needs to be a static method. Oh, it's [INAUDIBLE]? FILIP HRACEK: Yes. ANDREW BROGDON: Top level methods still make me feel weird. There's nothing wrong with them. They're very Darty and idiomatic to Dart. But coming from C Sharp and Java and other languages, I'm like, that just feels wrong. FILIP HRACEK: Can this be private? ANDREW BROGDON: Yes. FILIP HRACEK: And now, in this method we need to basically do everything. So we can do things like-- we get the message, which can be not just the send port, but later we will be sending other stuff. ANDREW BROGDON: You'll want to cache that. FILIP HRACEK: So if a message is send port, then I guess-- she would just do it all in one method? That feels weird. ANDREW BROGDON: So what are we actually going to do on this method? Are we going to listen for messages? FILIP HRACEK: Yes, and then delegate hopefully to those other methods. Not these ones. Basically what we're going to be doing [INAUDIBLE] somehow. ANDREW BROGDON: OK, we could start just by having it fetch the top story IDs and returning them just as a step. So this interesting concern. If we're talking about the idea of doing the work of parsing-- parsing is a tough job. It's going to take a long time. We want an isolate to move that parsing of JSON off the main thread. You could do that a couple of ways. You could have your main isolate download the stuff in the network, send it in a message to a long running isolate that's going to parse for you, and it could send back parse data. If you do that though, you're copying the data twice, because you're downloading it one isolate. You're copying it over here in a message. It's going to do some work. And it's copying it back when it's done. One of the better ways to do it is to have the secondary isolate do the download directly, and then do the parsing, and then send it back. And so you're only doing one big copy in that scenario. FILIP HRACEK: And so in any case, the isolate first needs to get the send port and save it somewhere, and then use that send port to send other stuff. And it also needs to get the data. And in our case, the data is basically the URL. We'll only give it like, hey, please fetch this URL for us, and maybe parse it. And then we can send the rest. So let's see if we are able to-- I just want to do send port send port, and [INAUDIBLE] message and return. ANDREW BROGDON: Didn't we just exit the function at that point with the return [INAUDIBLE]? FILIP HRACEK: And we're waiting basically-- if I'm not mistaken, we're waiting for our main isolates to be like, OK, so now that you respond, let me send you an isolate. ANDREW BROGDON: So you want a way to send the-- you need to send the isolate a message once it's started up, right? FILIP HRACEK: Yes. ANDREW BROGDON: One of the ways that people often do this is they have that-- so we've given the secondary isolate a send port to say, if you need to send me things back, here's how you do it. And so the secondary isolate needs to give our main isolate a send port. And so one of the patterns people use is the first thing that comes out of the send port that you give it is the send port to set things back. FILIP HRACEK: So we need this. And I think-- right? ANDREW BROGDON: No, so OK-- yes, you're right. FILIP HRACEK: So this is, again, dynamic, and then if message. I have a feeling we should extract this into maybe a class that's basically kind of wrapping the isolate for us like a worker. ANDREW BROGDON: Ideally yes, we would do that. We're already over halfway through this episode. FILIP HRACEK: Yes, OK. So if it's a send port, then we-- so we're basically doing the same thing, except on the main side. Send port as message. And then when we get that we know that we're ready for the other stuff to-- yes, so now that kind of breaks our [INAUDIBLE] code here. You know, because we're waiting for not the isolate. We're waiting for this send port. ANDREW BROGDON: I see what you're saying. FILIP HRACEK: Let's try it very quickly, class worker. And let's just do-- so when we create the worker, we will create the isolate. And then we can be like, isolate ready. ANDREW BROGDON: I see what you're saying. So we're going to define our own completer. FILIP HRACEK: Yes. ANDREW BROGDON: OK. FILIP HRACEK: So let me just quickly take this. We also need a void to close or dispose. Select kill. And then the receive port, we don't need, I don't think, but we do need the send port. And here we are like, we probably want the isolate. Like this. ANDREW BROGDON: And we're just like, OK. Not waiting on that. What's our analysis options for this project? [INAUDIBLE]? FILIP HRACEK: Well, yes. I think we're actually pretty-- ANDREW BROGDON: I don't think we have an analysis on this yet. FILIP HRACEK: We don't? OK. Anyway we have the isolate entry is here. Let's put it over there. It also lets me focus on just the isolate stop. So I think this is a better one. And then let's make all of this private. Nobody needs to know anything about what we're doing here. ANDREW BROGDON: Just as a general rule, whatever they do. FILIP HRACEK: Just don't. So we are coming back to this. We are awaiting an isolate. And then, when we get the message board back, which this is already implemented, we can be like, hey, the isolate is ready. So we have completer here. Isolate nobody. ANDREW BROGDON: Yes, I would put the type parameter on that and then change, complete the void to just final. FILIP HRACEK: Yes, [INAUDIBLE]. And then this basically just the isolate ready future. ANDREW BROGDON: You need a get, I think. FILIP HRACEK: Oh, yes. And then here, we are like, isolate ready is ready. So cool. We have the send port now and we can bail out of this. Return. Just waiting for another message. ANDREW BROGDON: Yes, you can do a return there. And you can do a [INAUDIBLE]. FILIP HRACEK: I think this should be handle message. Create the message. ANDREW BROGDON: May all our messages be handled. I'm enjoying your exquisite dynamics. It's nice. FILIP HRACEK: I'm glad you're enjoying them. ANDREW BROGDON: It's important. FILIP HRACEK: So if a message is something else-- ANDREW BROGDON: Wait, what's the actual payoff here? The real payload. FILIP HRACEK: All right, let's do the isolate entry. So isolate entry first gets a send port. This is my send port now. Oh yes, it should also-- ANDREW BROGDON: It needs to listen to its own send port. FILIP HRACEK: Well, shouldn't it create a receive port and then send it back? ANDREW BROGDON: Yes. FILIP HRACEK: OK, so we're doing the same thing on both sides. And this is why there's lots of helper methods like compute [INAUDIBLE]. ANDREW BROGDON: This is not a complicated pattern really. It's like, you get a pipe going one way. You got a pipe going the other way. A stream, basically, going one way and the other way. And so that's pretty much the case for any long lived isolate you're going to want to write for anything, because again, you don't really have to do isolates that often when working with Flutter. It's just not something we do much, which is why we're fumbling. There's definitely not any lack of skill on our part. FILIP HRACEK: Oh yes, we're great. We're doing great. So what I did here is basically the same thing. I create my own receive port. It's a hose that I give back to the main isolate and say, you can send anything. And as you can see, here in the handle message, when we get the send port we're like, cool, isolate is ready. And so now in the interest of time, maybe we'll just do one simple thing. So the isolate, when it gets this, that's [INAUDIBLE].. But then it also-- ANDREW BROGDON: What's up? FILIP HRACEK: I just want to-- right, yes. Now I need to just do this-- receive port and listen. And this is where basically all the magic happens. So this is another dynamic message. And we can just say, you know what, we know that what we'll get is going to be a string because it's going to be a URL. And then we'll just return the string capitalized or something. ANDREW BROGDON: Are we going to do the article IDs then for the-- FILIP HRACEK: I just want to make sure that the isolate actually works. So send port, send, message. ANDREW BROGDON: Or just send a message back. FILIP HRACEK: That's going to be-- flow analysis, come on. as string. ANDREW BROGDON: Two upper cases. There we go. FILIP HRACEK: So we have this. And then, handle message, if it's not a send port, we will just [INAUDIBLE] it. ANDREW BROGDON: Works for me. FILIP HRACEK: All right. And now we're going to add this completely unnecessary-- where was it? So we're going to create a new worker. So, final worker is a worker. Then we're going to await worker isolate ready. And then we're going to be like, hey worker-- ANDREW BROGDON: Did we define a method for this? FILIP HRACEK: Oh, we didn't. It just sends it. Wait, we didn't. Oh, yeah. ANDREW BROGDON: Yeah, we need to, like, fetch article IDs or something, fetch top stories, and then have that just send a string to the isolate. FILIP HRACEK: So that would be a void fetch IDs. ANDREW BROGDON: For now, yes. We'll want to change it to be a list of IDs later. FILIP HRACEK: Yes. And so there'll be a string URL. And that just says-- ANDREW BROGDON: So it needs to send the message into the isolate. So it'd say send port. FILIP HRACEK: So send port, send URL. ANDREW BROGDON: And we need to invoke that up above. And I think we can run this at that point. FILIP HRACEK: Fetch IDs. And that's going to be, hello world. ANDREW BROGDON: This is our URL. FILIP HRACEK: And that should just print how hello world capitalized. So let's set. See it in action. ANDREW BROGDON: All things being equal. FILIP HRACEK: Let me show you the debug thing. It's launching. Yes, so assuming this works, this is actually cool because now we have a worker class that can do anything for us. Hello world. ANDREW BROGDON: Boom. FILIP HRACEK: Boom. ANDREW BROGDON: We've successfully capitalized 10 characters. FILIP HRACEK: On a different thread, so that it doesn't block. ANDREW BROGDON: That's like, extra. FILIP HRACEK: Yes, and we get a malformed message. ANDREW BROGDON: That's interesting. Where did that come from? FILIP HRACEK: Yeah, it can't be us, right? ANDREW BROGDON: Getting malformed message in console when spawning multiple isolates. FILIP HRACEK: We are actually spawning multiple isolates, aren't we? We're spawning an isolate for each Hacker News tab. We could avoid that by doing something like the Hacker News [INAUDIBLE] has the worker. No. ANDREW BROGDON: You may have to-- pardon me as I just pull up the SDK. FILIP HRACEK: Wow, so in other news, we just got an exception in an isolate? What? In a spawn. Oh, because we have the compute still there. ANDREW BROGDON: We still have all the compute statements in place. OK, we should definitely get rid of those. That's not there. FILIP HRACEK: No, let's not do that. Wouldn't it be cool if we could just change that? But what we're going to do is good old cached articles ID is parse article. Story res body. We're still using compute in one place, and that is here where we're parsing story IDs, which we don't need to do that anymore. I mean, in the future we won't. ANDREW BROGDON: We can also just write a unit test for our isolate. [INAUDIBLE] the test out that way. FILIP HRACEK: Right. Let's see if we can get it back working without the malform thing when we rip out the compute method. ANDREW BROGDON: We're only spinning two isolates instead of-- FILIP HRACEK: Yes, I think it should be fine, but what do I know? Wait for it. Deserialization error. This is new. ANDREW BROGDON: There it is. That might be the-- where is it? FILIP HRACEK: That's a thing that I don't think has anything to do with us right now. ANDREW BROGDON: It's turned to [INAUDIBLE].. FILIP HRACEK: Where does it get the null? Oh, from-- ANDREW BROGDON: [INAUDIBLE]. So it's going through the list, right? And then scroll up. Its got-- FILIP HRACEK: [INAUDIBLE] ANDREW BROGDON: Right, and then one more. [INAUDIBLE] wait. Update article. So what's the ID there? [INTERPOSING VOICES] ANDREW BROGDON: --are not available. FILIP HRACEK: No, we're here. ANDREW BROGDON: What's the ID that it's got? And what's the story res dot body? FILIP HRACEK: Right, story res is-- ANDREW BROGDON: Content [INAUDIBLE].. I think that's the, we don't have an article for you thing. And so what's the URL it's got there? Story URL. Copy that. Stay in the browser. I bet you'll see that it's just copy value. And just go plug it into Chrome there. Shoot, that's not the full thing. FILIP HRACEK: But I can imagine-- [INAUDIBLE] a null to article failed. Yes, because we were calling iterator on [INAUDIBLE].. ANDREW BROGDON: I would just skip past this. Yes, because it's only happening for the one, otherwise we'd see a whole bunch of them. FILIP HRACEK: So this is another thing that we're [INAUDIBLE].. ANDREW BROGDON: Oh wait, or is it happening for all of them? FILIP HRACEK: Yes, the serializing-- No. See, this is why we have-- ANDREW BROGDON: So it's still just the one stack. We're just getting [INAUDIBLE] in multiple places. FILIP HRACEK: Right. ANDREW BROGDON: I'm curious why. FILIP HRACEK: It's possible that they made something nullable that wasn't nullable before. And we should update the API. But no, this is a null. It's weird. I mean, maybe it's just a flaky [INAUDIBLE] on [INAUDIBLE] and that's why we want to have some error handling. Maybe you know the episode. But now, it makes our life harder. As you said, maybe we should unit test. ANDREW BROGDON: I think so, because we're over 45 minutes now. We can certainly do a part two of this, which I think is a good idea. But we should have a test that's like a worker test. FILIP HRACEK: All right, let's start it. And so that would be-- ANDREW BROGDON: You're more hardcore than I am. I would have just copied the test out of some other filed and ripped the guts out of it. FILIP HRACEK: You're probably right. What do we want to test? ANDREW BROGDON: Capitalize this correctly. You can start with that. FILIP HRACEK: It doesn't actually-- worker-- so final worker is worker. And then [INAUDIBLE]. ANDREW BROGDON: So we don't wait. It's ready. FILIP HRACEK: --worker is ready. And it should be as ready. You're right. And then worker fetch IDs, hello. And now we can't actually-- it doesn't return anything. So we should change that. But before I do that, worker expose. And now, fetch ID is actually a future of-- ANDREW BROGDON: Just make it a list events for now. We can just return the-- FILIP HRACEK: Yes, that's probably the best. ANDREW BROGDON: We can just return it [INAUDIBLE] or something. FILIP HRACEK: OK, what do we do? [INAUDIBLE] And then we probably need another completer on a top level. ANDREW BROGDON: Yes, this is a question. How do we keep it from doing multiple prints at once? FILIP HRACEK: Well, we would just be like, if there's another request, just like-- ANDREW BROGDON: [INAUDIBLE] FILIP HRACEK: Yes, so here's a completer of list of [INAUDIBLE] IDs. Now, if IDs is a null, return. ANDREW BROGDON: Except we'd want to-- FILIP HRACEK: Yes, maybe we will do override. ANDREW BROGDON: [INAUDIBLE] FILIP HRACEK: So IDs is a new completer of this event. And basically, we just return IDs future. And then here, we were-- ANDREW BROGDON: We're just printing it now. FILIP HRACEK: Yes. This is actually a list of ints. We need to do that. So if message is list of ints, then we're like, [INAUDIBLE].. ANDREW BROGDON: If IDs is a completer, which it should be. FILIP HRACEK: IDs complete with message. ANDREW BROGDON: And I would do a garden. Do a conditional method access on that complete just in case. You had a question mark. FILIP HRACEK: Oh, yes. ANDREW BROGDON: And [INAUDIBLE] it out too, before you return. And actually, we don't [INAUDIBLE].. FILIP HRACEK: So we could actually be like, throw here. What's going on? ANDREW BROGDON: I would just let it pass. And this too shall exit. FILIP HRACEK: OK, let's try it. So we are not actually expecting anything from here. So final result is-- ANDREW BROGDON: You can just do an expect there. Expect worker dot fetch IDs hello. And then do completes or completion. All my Dart path testing work, while I'm trying to copy this API for Dart path. And then its completion. And then give it, what's the-- we're expecting a list of ints this point, right? FILIP HRACEK: Yes. ANDREW BROGDON: What are we returning out of the-- FILIP HRACEK: We're not. I think that we're done. Where's the isolate? So the isolate, if it's-- it gets a string, which is like a URL. ANDREW BROGDON: So that needs to be just the list of ints that we give it, right? Just a constant 1, 3, 4. It's a wonder I get paid. FILIP HRACEK: So worker returns. And then this one. ANDREW BROGDON: That should be 1, 2, 3. I believe that'll work. Completion should take care of it. FILIP HRACEK: Let's test in tests. ANDREW BROGDON: Did that pass? FILIP HRACEK: Yes, it did, didn't it? That was pretty cool. Some things that just work. ANDREW BROGDON: That's the thing, I don't think it. It was too easy. [INTERPOSING VOICES] FILIP HRACEK: Sometimes, like, I'm a little, so-- ANDREW BROGDON: Right-click on it. FILIP HRACEK: Just run this one, please. It still spins up [INAUDIBLE],, which is what we wanted. [INTERPOSING VOICES] ANDREW BROGDON: By the same metric, yeah. FILIP HRACEK: All right, so I think we have a worker. Let's summarize. We're in the 15 minute mark. We have a worker, which we're actually-- oh wow, we're using it here, never killing it. ANDREW BROGDON: Good times. FILIP HRACEK: Yes, but we have that. And now it should be pretty easy for us, here in the refresh method, to be like, hey first get me the IDs and just wait for them, like we were waiting here. This is terrible. This is old code. It's basically just a wait for when it's ready, and then a wait for fetch IDs of some URL. And that just will happen on the isolate. And then we can use the same worker for actually parsing the articles. So I think it's cool. ANDREW BROGDON: I think the lesson here is that we probably should have started with the unit test. FILIP HRACEK: Yes, that's a good one. ANDREW BROGDON: Which is never how I do it the first time. We should probably have a part two of this episode, right? FILIP HRACEK: I wonder if I should just implement this now off record and then come back to it, because we had another idea. ANDREW BROGDON: We do, but we do one of these every two weeks. When it comes out, people can leave comments about whether they want to see the rest of this or whether we should just scuttle off and do it and fix it that way. But I think there's some interesting things we still need to figure out, like what are we going to do if-- right now we have that future. And if you call fetch and the future is in place, it just returns nothing, right? FILIP HRACEK: In the worker? ANDREW BROGDON: In the method to-- FILIP HRACEK: You mean fetch IDs? ANDREW BROGDON: Fetch IDs, right? It returns the future there, which means we're going to stomp on the existing future right there. If somebody calls fetch IDs, and then later we call fetch IDs again, without waiting on the original future, we stomp all over this. There's some details of the interface that we might-- And those are interesting details, which can be explored, or it could just be run off and done. FILIP HRACEK: Right now, we're like, we don't care about the future anymore. That's not nice, but it's probably something-- ANDREW BROGDON: Right now if you call fetch IDs twice, it sends both of those requests in. And it makes the completer at the first one. Then it sends the next one. Then it goes, that completer from before, I no longer care about it. I'm going to overwrite it, which means the first call to fetch IDs, that future will never complete, which is probably not how we want to do this in the long run. We need to figure out a way to sensibly manage that. FILIP HRACEK: I'm fine with that, because you have a worker. And if you're parallely fetching IDs from one worker, then you're [INAUDIBLE]. ANDREW BROGDON: It's on you to-- FILIP HRACEK: It's on you for breaking our code. Anyway, thank you for watching. And the next episode, hopefully we'll get back to this because I enjoyed it, and get your code on a offline isolate, because otherwise you'll have really janky apps. ANDREW BROGDON: Well said. Bye everybody. FILIP HRACEK: Bye. [MUSIC PLAYING]
Info
Channel: Flutter
Views: 34,651
Rating: undefined out of 5
Keywords: HackerNews, Dart, isolates, isolate class, multithreading, isolates and multithreading in Dart, multithreading in Dart, JSON, flutter, flutter 101, build your app with flutter, google, github, build an app, google developers, the boring flutter development show, the boring show, GDS: Yes;
Id: qrFTt1NZed8
Channel Id: undefined
Length: 59min 59sec (3599 seconds)
Published: Fri Sep 27 2019
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.