Check out a free preview of the full Basics of Go course
The "Async API Calls with GoRoutines" Lesson is part of the full, Basics of Go course featured in this preview video. Here's what you'd learn in this lesson:
Maximiliano demonstrates the creation of a lambda function that functions as a GoRoutine, allowing for the containment and synchronous execution of API calls. This approach enables efficient handling and coordination of API requests within the Go program.
Transcript from the "Async API Calls with GoRoutines" Lesson
[00:00:00]
>> So what if we create a lambda function? What's a lambda function? A function that we create on the fly without a name. So we create a function, okay? Let's see like this, and then we have, Before that function, and we call go, Like this here, okay? And, Then we wanna execute Done here, wg.
[00:00:41]
Did they change anything in particular? Well, not yet, not yet, that's why I'm going to refactor this. So I'm creating a function that, am I calling the function? No, in fact, it's complaining that. If I look at the code, it's complaining that I have a function that is not used.
[00:00:58]
So I have a literal function that is not used, no one is using this. How can I use a literal function? I can execute it immediately right now. This is an immediate execution literal function. This was a pattern pretty common in JavaScript a few years ago, okay? That right now is not so common, but you're gonna feel fine.
[00:01:18]
So we create a function, and then we execute the function. What's the difference at this point? No difference, no difference. But, Now, what if we change the go, that go call. Did you hear that? She said go. So he got the answer. So we moved the go there.
[00:01:47]
So now we create a new goroutine with this function that will synchronously execute, get currency data, and when you finish it will call Done. And because this function, it's within the other function. So this literal function is inside the other one, go supports closures. So it can access local variables that are part of the outer function.
[00:02:18]
So I can access wg. Okay, makes sense. Yeah, I mentioned that this will be a little hacky. It's not yet done, though. So, if I execute this, I got something, but not exactly what I was expecting. I got three responses, but the three for Bitcoin Cash. So if then go fan of Bitcoin Cash, that's not the thing because I might change the order here in the array.
[00:02:59]
If I run this again, I'm getting ETH. So it seems like for some reason, now I'm getting only the last. Does anyone know why?
>> Changing the value of currency each time you go through that loop.
>> Something like that, in fact, can you see that we have a warning there, we have a yellow underline.
[00:03:26]
It's already saying, hey, be careful, maybe you have a problem. So what's going on here? This is a for, okay, and closures in go works in this way. When you have a closure and what's a closure, the access of a local variable that is actually from the outer function.
[00:03:43]
So in this case currency, it's a local variable that was defined in the outer function. So I mean this function and this is main is the outer function, okay? So we have two functions. We're getting kind of not a copy of the value, but the reference of the value.
[00:04:04]
So, getCurrencyData has a reference to the currency variable, okay? So, we have a for, a loop. We are immediately creating and launching all the goroutines, immediately, okay, one by one. So when each goroutine it starts the process and goes to a network, when they go and read the currency property, the currency property is actually the last one, because the for already completed the loop.
[00:04:38]
Does it make sense? It takes time to understand what's going on. And always, when you work with threats, it's difficult to think about what's going on, it takes time to get used to that. But the idea here is that, the problem is that we are actually looking at the currency, and it's not a copy, so each goroutine has access to the same currency variable, because we don't have different memory spaces per goroutine, it's the same memory space.
[00:05:15]
So, they all access to the same currency variable that when the for ends, it's in the last one. Makes sense? I'm probably thinking, but why is it still alive, the variable, because actually, the for ends. If the for ends, well, the currency is only the for. Well, when you have a closure, the closure is doing that.
[00:05:39]
If you have a function that has a closure, it's actually like grabbing that variable and keeping that variable alive for more time. Anyway, to come to solve the problem.
>> Cast or make a clone of the string.
>> Make a clone of the string. So what, talking about cloning or copy values, do you remember any ability in go that is doing that by default, cloning variables, copying variables?
[00:06:11]
>> If it's defined outside-
>> It is defined outside because currency is defined outside.
>> [INAUDIBLE] pointer.
>> Pointer, but we have a pointer problem. Yeah.
>> Function params are copy.
>> Params, function arguments. When you call a function, remember, when you call a function, the value that you pass as an argument will be copied, will be cloned.
[00:06:37]
What if when you say, okay, where do I have an argument here? Maybe, I can receive one in my literal function. So I can receive, let's call that currencyCode just to change the name, there's going to be a string. And then I'm going to use that currencyCode that it should be a copy.
[00:06:57]
But now I have an error because it says, hey, that function, the literal function receives an argument, so I'm going to pass the currency. Again, if I show you this code without explaining it, it takes a while. What's going on, okay? So I'm pretty sure that some of you are still thinking what's going on even with all the steps one by one.
[00:07:20]
But actually, let's try to review what's going on. First, let's run this. So let me run this. Now, it's back. So it's working back and now it's not waiting like three seconds, four seconds, the waiting group is working. So that wait, that w waited that we have there, will stay there until the working group is done.
[00:07:43]
Remember, the working group is just a counter. The problem is that we need to understand how to add one and how to call Done. Done is when that one is done. And it's a little tricky to understand how to do that when you have just one code that is kind of executed in different goroutines.
[00:08:03]
That makes sense? Yeah, within the chat.
>> Can we refactor it to a new function since we are sending via params.
>> We can, yeah, in that case we won't have the wg variable. So you need to find a way to pass that wg as an argument or define that as a package variable, as a global variable.
[00:08:28]
Because here we are taking advantage of we can call wg done because we are also using a closure. But yeah, you can refactor this. You just need to understand what's going on with goroutines. And in this case, we mix also the idea of a closure and how closures work.
[00:08:48]
And if you're in a loop, inside the closure, you will always get the last value, the last index, unless you make a copy of the value when you are looping that element. That was a little hacky, I know. Do you have any questions? Yeah.
>> It looks hacky, but is this a common pattern, a common way to solve this.
[00:09:15]
>> If it is the code is, I will say simple enough, yes. Maybe you think it's not simple, but this is kind of simple, but it's it gets more complicated. Yeah, it's virtual, like move this to a different pattern. But anyway, it's important to first start with something simple to get the idea, because if not, it gets complicated understanding routines.
[00:09:36]
And that every goroutine is actually in the same share space, because it's like, initially we think that every goroutine we have its own set of variable, and that's not true.
Learn Straight from the Experts Who Shape the Modern Web
- In-depth Courses
- Industry Leading Experts
- Learning Paths
- Live Interactive Workshops