Fuck callbacks! Let's use generators
Posted on January 18, 2015 • 2 minutes • 314 words
Let’s write a simple function hello
that return a string when called.
var hello = function () { return 'Hello ' + name; }
Now convert it into a generator. Call hello()
this time will return you an Object
instead: the generator.
var hello = function *() { return 'Hello ' + name; }
Let’s consider the following snippet.
var hello = function *() {
yield 'Stopped here!';
return 'Hello ' + name;
}
var generator = hello('Clark Kent');
console.log(generator.next()); // {value: 'Stopped here', done: false}
console.log(generator.next()); // {value: 'Hello Clark Kent', done: true}
So what good can generators do for me? Generators can help eliminiating callbacks. Using callback for 1 or maybe 2 levels is fine. However, imagine if you have to use callbacks for 3 levels or more like the example below. URGH!!
func_1(arg, function(err, result) {
if (err) { .... }
else {
func_2(result, function(err, result2) {
if (err) { ... }
else {
func_3(result2, function(err, result3) {
if (err) { ... }
else {
// LOST IN LIMBO !!!
}
});
}
});
}
});
Take an example: consider a scenario where you have to establish connection to a bunch of database servers at application startup. With generators, you can do something like this:
var init_all_connections = function *() {
var result;
result = yield init_connection(conn_str1);
result = yield init_connection(conn_str2);
result = yield init_connection(conn_str3);
// ...
}
For the sake of conveniency, you can write a function to execute the generator until done
is true
, throw
if an error occurs. So the whole thing looks as simple as this:
var init_all_connections = function *() {
var result;
result = yield init_connection(conn_str1);
result = yield init_connection(conn_str2);
result = yield init_connection(conn_str3);
// ...
}
// execute
execute_generator(init_all_connections());
Look how the code is cleanly organized and so much readable compare with the callback hell above. Ain’t you glad you read this now ;)