1. What is the difference between synchronous and asynchronous functions?
Synchronous functions must completely be executed before moving on to the next computational process in the event loop. The event loop is what keeps track of the tasks JavaScript has yet to complete in a program, think of it like a to-do list for JavaScript. Once the block of code containing your synchronous function has fully executed, Javascript will move on to the next item on the list.
Asynchronous functions do NOT have to fully complete before moving on to the next item in the program. This allows them to be initiated and then put on hold while the function waits for the output–like the output form a API call–to be returned before moving on with the code associated with that particular operation. While this process of initiation and consequential waiting is going on, JS can put the whole asynchronous event on hold as it completes other functions and computational operations. Then, when the output is received, JavaScript will jump back to finish executing the asynchronous function it put on standby before moving on to finish executing the rest of the program. Essentially, asynchronous code can be thought of as happening in the background.
This setup allows JavaScript to execute multiple operations at once, while simultaneously acting as though it was operating in a single threaded environment–an environment that only takes care of one task at a time.
2. What is callback hell?
Here is an example of some code written in the callback hell format:
trackUser = function(userId) {
users.findOne({userId: userId}, function(err, user) {
var logIn = {userName: user.name, when: new Date};
logIns.insert(logIn, function(err, done) {
console.log(‘wrote log-in!’);
});
});
});
A situation where there are several nested functions that all rely on the callbacks of the parent function in order to run…essentially this means that the program includes an overarching parent function that has child functions which will run based on the events of the parent function first being executed. Although this all runs fine, and is a way to allow the code to execute other tasks asynchronously while waiting for the callback events to execute (and subsequently call their associated functions), it can very quickly get messy and confusing for anyone trying to decipher the code…even the developer who wrote it!! Thus this messy coding style earned the name “callback hell” in honor of the hellish conditions created for all those who try to understand the verbose asynchronous coding style. Luckily, there is a better way!!!
*Side note: Here is a definition of a callback function in Jake terms:
a) Composed of 2 components:
- Callback event - a condition that must be met in order for the associated/linked callback function to be summoned. Without this even being executed, the associated callback function will NEVER run.
- Callback function - a function–generally an anonymous function–that performs some action based on a certain event taking place. This function will remain dormant in it’s parent function until the associated event fully completes. However, after the callback event’s condition is met, JS will immediately jump to that line to execute the callback function.
b) Callbacks are important in the control flow of a program. They allow a task (callback event) to be initiated and ignored by the JS engine until the task is fully completed. During this ignored period, JS can execute other tasks it has waiting in it’s que acting out code asynchronously (in the sense that while this one event is waiting to be completed, other events can also be completed simultaneously). After the task (callback event) completes, the JS control flow immediately jumps back to the previously ignored callback event to complete the associated callback function that was waiting for the event to finish up in order to execute. After it’s done executing, JS will resume where it left off in executing the rest of the other code in the program.
3. Which technique can help us solve callback hell?
Of course there’s a better way! Promises are one of the keys JavaScript developers use to help them overcome the problems associated with ~callback hell~. So what exactly do they let us do? This simple built-in jQuerry library allows us to chain callbacks and better deal with errors. However, now there is an even better technique available in the Fibers package of Meteor.
Fibers extends the run time so you can abstract away asynchronicity to write code that looks synchronous while still running within an asynchronous environment. You can find this package in the “Future” sub-library. For example, you could change the asynchronous function (setTimeout) to the synchronous equivalent of (wait) using Fibers. This is just simulated synchronicity, so you still will find all the benefits of asynchronous execution (because that’s what’s really happening) without the hassle of having to write in a form that yields callback hell…instead you’ll have the freedom to write with a synchronous control flow in mind.
Below, you’ll find the code that was written above cleaned up and made easily readable by implementing the “Fibers” package below:
trackUser = function(userId) {
var user = Meteor.users.findOne({userId: userId});
logins.insert({userName: user.name, when: new Date});
console.log(‘wrote login!’);
}