Callback hell is typical problem when we have more then two asynchronous calls. I this article I will try to show worst and best practices.
In all examples I am using startjs library for easy write result in html. Library is available in github – https://github.com/StartCodingPL/startjs. All example are using ES6 syntax so best browser to check it working is newest chrome.
Callback hell example
On first attempt I will show the worst possible ( I think ) solution. For async calls I will be using setTimeouts but in real apps it can be everything and mostly it will be ajax call.
So, as You can see code is quite not readable, it has many nested async calls, such call is difficult to manage, and imagine if we have few more async calls, then it will be real hell to fix it or change it. In real examples callback function can be more complicated so it would look even worst.
First thing which we can change is to create functions for every call. So check upgraded example.
Above example has much cleaner code, it uses function attributes to send callback function inside setTimeout. This is common approach but has one big disadvantage – callback calls are nested and we have many }) signs, so it is difficult to see which function is ending where.
Promise – the better and cleaner way
Nesting callback is no solution, first solution which I will show is Promise, promise is used widely in many js libraries like Q, Bluebird, Jquery Promises and many more. So what is really this Promise thing? In simplest word it is something which will call next function when current promise will be resolved. If we think about some real life algorithms We can take for example restaurant flow, where the cook gives promise to the waiter for that he will prepare a meal, and this is our promise, waiter don’t do anything before this promise is resolved by the cook and when cook resolve it then waiter starts his part. In return to technical explanation – big advantage is that resolve can be called not only after one async call, but it can be called after many calls.
I will show promise in ES6, it is builded core functionality in ES6, it not needs any third part libraries.
It is big change for better, I will try to compare two last examples.
has been changed to:
ES6 generators – new approach to asynchronous code execution
So promise is a solution and any library based on this approach also is a solution for this problem, but ES6 give us maybe better one – ES6 generators.
ES6 generators enable to create synchronous looking code which is really asynchronous. Strange but it really works!
Most important thing here is to use * as set that function is generator, without it yield keyword will give an error. So how it is working – every next() method call is running code in generator from last stop to next yield word, and it stops there, so We can really stop code execution and run it again by g.next. Check that g.next() is called in timeouts, it is resuming generator execution.
Let look on the last async calling in generator version.
It looks like synchronous code but every yield is like breakpoint for code execution. Lets think about flow in this example:
- g.next – runs asyncName() and stops
- in asyncName g.next runs code from last stop to asyncLastName()
- in asyncLastName g.next runs code from last stop to asyncAge
- ….I think You know the rest
Conclusion – we have two good solutions – use promises or use generators. I don’t want and I don’t need to choose which one is better, I think it always depends on situation. Good practice will be to choose one of this two and use it widely in project, using it together can be confusing because of big differences between them.