fbpx

Callback methods can also be either synchronous or asynchronous and add a whole new dimension to how you can approach problems.

The way I think about callbacks is like this.

There’s two sides to a callback. One side defines what the callback should look like. In other words, what are the parameters and return type. This is the side that actually calls the callback method.

Then there’s the side that implements the callback method. The callback method needs to follow the guidelines set out by the side that’ll be making the call.
After all, if the method doesn’t match, then it can’t be called.

So whatever code is actually making the call gets to decide what the method will look like. The whole point of callbacks is to give control to the developer to do practically anything inside the callback. This means that you can normally think of a callback method as a method that you write but that you can give to some other code to actually run.

You have synchronous callbacks that never leave your thread. These are just like calling a method that then calls back one of your own methods.

And you have asynchronous callbacks that run in their own thread.

Listen to the full episode for more or you can also read the full transcript below.

Transcript

If you haven’t already listened to the previous episode, you’ll probably want to do that first. This episode continues the sync/async discussion.

The way I think about callbacks is like this. There’s two sides to a callback. One side defines what the callback should look like. In other words, what are the parameters and return type. This is the side that actually calls the callback method.

Then there’s the side that implements the callback method. The callback method needs to follow the guidelines set out by the side that’ll be making the call.

After all, if the method doesn’t match, then it can’t be called. So whatever code is actually making the call gets to decide what the method will look like.

You may find at least at first anyway that you’ll mostly be implementing callback methods and passing them to some other code to be run.

It’s not about your experience level of anything. Although designing a good system that other developers can then use to write their callbacks is more complicated. It’s more about the need. The whole point of callbacks is to give control to the developer to do practically anything inside the callback. To me, that just means that there’s a lot more uses that you can apply callbacks in your code than there are places where you’ll need to be calling them.

This means that you can normally think of a callback method as a method that you write but that you can give to some other code to actually run.

A good example of this in real life is a company’s standard operating procedure manual. This is a book that lists exactly how to do things step-by-step and is given to employees to follow. It’s usually written by somebody in the corporate office headquarters. If you think about it, a procedure written like this is just like a method in programming. This is a method that was written by one person to be followed by another person.

You can do the same thing in programming and then some. Because in programming, you can send your method to some other code that’ll run it later like I just described or you can also send your method to code that’ll run it right away. This gives you a whole new way to think about solutions to problems and allows you to write code that’s more widely applicable. What do I mean?

Let’s say you have a bunch of numbers that you need to add together and these numbers are in a collection. By the way, episodes 39 through 46 are all about collections. Alright, so you have your collection. What do you do? Well, you go get the first number then the second and add them, right? Then you go get the third number and add that. And you keep going until you’ve added all of them.

This works. But then you need to print them. You could add the printing code right next to the adding code. But then you realize that you don’t always need to add them. Sometimes just printing them is enough. And just when you almost have this all figured out, you get a new requirement to sometimes multiply them. Oh, and since this is working out so well, your manager now wants you to do the same thing in a bunch of other code that uses different collection types.

What you need is a way to separate the navigation of the numbers from the operation that you want to perform. And it turns out that the collection classes have already thought of this and provide exactly what you need. I’ll explain how it works right after this message from our sponsor.

( Message from Sponsor )

Most collection classes should have a method called forEach or something like that. The method does exactly what it says it will and goes to each item in the collection and does some work. The only problem is this method has no idea what task you need to perform. All it knows is how to visit each element in the collection. It’s up to you to write a method to be run for each element.

So what you do is write the method exactly the way the collection class expects. You need to do this because the collection will be the one actually calling the method. You write the method and the collection class calls it however many times it needs to. This means that you have to write this method so that it can be ready with its final answer at any time. It should keep a running total in the case of adding all the numbers. Or it should print each number individually if you need to print all of the numbers. Regardless of what you need to do, all you have to do is provide the specific set of instructions that the collection class can call as many times as it needs.

This is a callback method. You write the method and the method lives in your class. The collection class calls back into your method when it needs to.

This is also a synchronous type of callback although it doesn’t have to be. For something like adding all the numbers you might get a lot of lock contention if you tried to call a parallel version of forEach that divides the numbers to be added into groups to run on multiple cores. If there was a lot more work involved in whatever you need to do for each item in the collection so that the accumulation of a single answer was just a small portion of the overall work, then maybe it would work out better. But if each thread tries to get the same lock over and over just to add an individual number and then release the lock, then this could actually perform worse than if you just added all the numbers on a single thread.

Sometimes a callback like this is exactly what you need. It gives you the ability to write a method that will do just the portion that you care most about.

This is similar to the template method design pattern from episode 80. But there’s a difference. The template pattern is all about leaving room for derived classes to customize certain aspects of a more complicated process. A callback is more open to your needs.

Callback methods can also be asynchronous which means that the method gets called from different threads. A good example of this is a timer. You create a timer when you want to do something in the future. Now this is not like being reminded to do something although you could implement it like that. You see, when somebody reminds you to do something, you’re still the one doing the work. A timer callback is different. You write the code that will be run at the appropriate time but you don’t run the code directly in your thread. In other words, you never actually call the callback method directly from your code. You only pass the method to the timer.

This is what I mean by callbacks being more open than the template design pattern. What you do in your timer callback is completely up to you. Just remember that your code continues to run whatever methods it’s calling and the timer callback will be run in a different thread. Your callback will need its own locks if necessary.

I mentioned that you could have the timer implemented in such as way that it’s more like a reminder and then your thread does the actual work. How would you do this? In this case, you wouldn’t do the work in the timer callback. All you do in the timer callback is either signal an event or post a message to a queue of some sort.

If you decide to use a signal, then that means your main thread will need to sometimes wait on that signal. When you get the signal, it’s your main thread that’s running and you can do whatever work you wanted to do at that time.

And if you decide to use a queue, then your main thread will need to periodically check the queue to see if there’s anything there to be done. Again, you’re back in your thread and no longer in the callback method.

Or you can just do whatever needs to be done in the timer callback right from the callback.
Either way is up to you. Remember that you have:

◦ Synchronous callbacks that never leave your thread. These are just like calling a method that then calls back one of your own methods.
◦ And asynchronous callbacks that run in their own thread.

Before we end this episode, I just wanted to point out that the entire mechanism that allows one thread to create another thread is based on callbacks. You see, when you want to start a thread, that new thread needs some instructions to run, right? Where does it get those instructions? If it’s your thread, then it should be running your code. So you write a callback method that follows the guidelines set out by your language library which in turn just follows the guidelines set out by your operating system. You then call a method to start the thread and pass your callback as one of the parameters. When the thread gets scheduled to run, it’s actually running your callback method.