[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]
It was very boring, I loved it, thank you!
So you went 20% over time and decided to speed up the video by 20% to fix it? β€οΈ