On Github robert-chiniquy / how-to-write-javascript-when-youre-not-drunk
You probably already think you know how to write Javascript when you're not drunk. But I hear you hating.
It's ok to have written some bad Javascript. No one writes the pure, innocent Javascript of Eden. We are all flawed.
When you wake up and realize what's happened, your friends are the ones who help you take care of it.
apiKey = loadConfig(); userId = authenticateUser(apiKey); client = new ApiClient(userId); info = client.getUserInfo(); writeResponse(client.format, serialize(info));
var apiKey = fs.readFileSync(SETTINGS).apiKey; var client = new ApiClient(); var info; async.waterfall([ function(callback) { authenticateUser(apiKey, callback); }, function(callback) { client.getUserInfo(function(err, result) { info = result.info; callback(); }); }, function(callback) { writeResponse(client.format, serialize(info), callback); } ], callback);
var apiKey = fs.readFileSync(SETTINGS).apiKey; var client, info; async.waterfall([ function(callback) { authenticateUser(apiKey, callback); }, function(result, callback) { // can't create the client until we have the userId client = new ApiClient(result.userId); client.getUserInfo(function(err, result) { info = result.info; callback(); }); }, function(callback) { writeResponse(client.format, serialize(info), callback); } ], callback);
function(callback) { if (!userId) { info = defaultInfo; callback(); return; } // can't create the client until we have the userId client = new ApiClient(userId); client.getUserInfo(function(err, result) { info = result.info; callback(); }); },
function(callback) { writeResponse(client.format, serialize(info), callback); }, function(result, callback) { reportMetrics(result.latency, result.statusCode); }
apiKey = loadConfig(); userId = authenticateUser(apiKey); client = new ApiClient(userId); info = client.getUserInfo(); writeResponse(client.format, serialize(info));
var apiKey = fs.readFileSync(SETTINGS).apiKey; var client, info; async.waterfall([ function(callback) { authenticateUser(apiKey, callback); }, function(callback) { if (!userId) { info = defaultInfo; callback(); return; } // can't create the client until we have the userId client = new ApiClient(userId); client.getUserInfo(function(err, result) { info = result.info; callback(); }); }, function(callback) { writeResponse(client.format, serialize(info), callback); }, function(result, callback) { reportMetrics(result.latency, result.statusCode); } ], callback);
There's a cliff you hit in complexity, sometimes very late at night… and you might just add one commit too many and lose control.
async.waterfall([ authenticateUser, client.getUserInfo, writeResponse ], callback);
Just a thought. An async workflow is a state machine. A class is a collection of state and operations upon that state. Consider just writing a class for each workflow, exposing each step as a method, and keeping all shared state in the class instead of embedded in lexical scope.
The methods will be individually testable, tests can create fixture instances of the class, and you can use dependency injection within the class to mock external resources.
Assume the ideal method signatures and return values, write the async workflow first, and write your class later
Just one possible way to go:
async.waterfall([ this.auth.bind(this), this.getUserInfo.bind(this), this.writeResponse.bind(this) ], callback);
(too bad about having to bind `this`)
Oh, and never use `async.waterfall`, it tightly couples callback parameters to method signatures. Use `async.series` or `async.auto`.
async.series([ this.auth.bind(this), this.getUserInfo.bind(this), this.writeResponse.bind(this) ], callback);
Each method can encapsulate the weirdness required for the code it interacts with. You could even try using polymorphism for type dispatch instead of conditional statements inside them.
Inline anonymous functions sometimes increase clarity
Javascript isn't Java or Ruby— seek the most consistent idiomatic expression
Sometimes you can remove abstractions and layers of callbacks to find some very simple code
The simplest code is likely the most correct.