Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to avoid piling up callbacks or "callback hell"?

Tags:

My program makes heavy use of (possibly) asynchronous calls where the return value is not immediately available, thus there are a lot of methods like this:

// A simple callback interface
public interface GetFooCallback
{
    void onResult(Foo foo);
};

// A method that magically retrieves a foo
public void getFoo(long fooID, GetFooCallback callback)
{
    // Retrieve "somehow": local cache, from server etc.
    // Not necessarily this simple.
    Foo foo = ...; 

    callback.onResult(foo);
}

However, as there are lots of things that are dependent of the last call like this, they start to pile up:

// Get foo number 42
fooHandler.getFoo(42, new GetFooCallback()
{
    @Override
    public void onResult(final Foo foo)
    {
        // Foo 42 retrieved, get a related Bar object
        barHandler.getBar(foo.getBarID(), new GetBarCallback()
        {
            @Override
            public void onResult(final Bar bar)
            {
                // Send a new Zorb(foo, bar) to server
                zorbHandler.sendZorbToServer(new Zorb(foo, bar), new ZorbResponseHandler()
                {
                    @Override
                    public void onSuccess()
                    {
                        // Keep dancing
                    }

                    @Override
                    public void onFailure()
                    {
                        // Drop the spoon
                    }
                });
            }
        });
    }
});

This "works", but it starts to feel rather icky when the pile keeps growing and it's difficult to keep track of what's happening. So, the question is: How do I avoid this pile-up? As I've googled "callback hell", many places suggest RxJava or RxAndroid, but I haven't really found any examples showing how one would convert something like the aforementioned example into a more concise whole.

like image 742
manabreak Avatar asked Sep 16 '15 13:09

manabreak


People also ask

What is callback hell and how can it be avoided?

Callback hell in Node. js is the situation in which we have complex nested callbacks. In this, each callback takes arguments that have been obtained as a result of previous callbacks. This kind of callback structure leads to lesser code readability and maintainability.

What is callback hell how do you solve it?

JavaScript provides an easy way of escaping from a callback hell. This is done by event queue and promises. A promise is a returned object from any asynchronous function, to which callback methods can be added based on the previous function's result.

What is callback and callback hell?

The callback function passed is used to sum up the elements of the array. After 2 seconds have passed, the sum of the array is printed which is 9. Callback Hell: Callback Hell is essentially nested callbacks stacked below one another forming a pyramid structure.

What is callback hell and how you will avoid with code snippet?

Callback Hell, also known as Pyramid of Doom, is an anti-pattern seen in code of asynchronous programming. It is a slang term used to describe and unwieldy number of nested “if” statements or functions. If you are not expecting your application logic to get too complex, a few callbacks seem harmless.


1 Answers

This is a controversial topic with a lot of opinions; let me focus on a specific solution and try to argue why it is better than callbacks.

The solution is usually known as future, promise, etc; the key point is that an async function does not take a callback; instead, it returns a future/promise that represents the ongoing async action. Here, let me use term Async to represent async actions because I feel it's a much better name.

Instead of accepting a callback

    void func1(args, Callback<Foo>)

return an Async instead

    Async<Foo> func2(args)

Async does contain methods that accept callbacks on completion, therefore func2 could be used in the similar fashion as func1

    func1(args, callback);
    // vs
    func2(args).onCompletion( callback )

In this regard, Async is at least not worse than the callback solution.

Typically, Async are not used with callbacks; instead, Asyncs are chained like

func2(args)
    .then(foo->func3(...))
    .then(...)
    ...

The first thing to notice is that this is flat, as opposed to callback nesting.


Beyond aesthetic reasons, what's the big deal of Async? Some people argue that it is essentially the same as callback, just with an alternative syntax. However, there is a big difference.

The biggest secret of OOP is that you can use objects to represent stuff... How is that a secrete? Isn't that OOP-101? But in reality people often forget that.

When we have an Async object to represent the async action, our program can easily do stuff with the action, for example, to pass actions around through APIs; to cancel an action or set a timeout; to compose multiple sequential/parallel actions as one action; etc. These things can be done in the callback solution, however, it is just a lot more difficult and non-intuitive, because there are no tangible objects that the program can play with; instead, the concept of action is only in our head.

The only real judge is whether a solution does simply your application. Here is my async library, take a look and see if it helps.

like image 93
ZhongYu Avatar answered Sep 20 '22 15:09

ZhongYu