Skip to content

Instantly share code, notes, and snippets.

@spion
Created September 18, 2012 13:41
Show Gist options
  • Save spion/3743154 to your computer and use it in GitHub Desktop.
Save spion/3743154 to your computer and use it in GitHub Desktop.
// The original function. Nothing wrong with it. Its short and simple.
// The code flow is obvious.
function myfunction(response) {
db.stuff(function stuff_cb (results) {
var transformed = trans(results);
db.stuffWith(transformed, function stuffWith_cb(endResult) {
var finalTransform = final(endResult, transformed);
response.end(finalTransform);
});
});
}
// Naming your callbacks to keep nesting shallow, problem #1
function myfunction(response) {
db.stuff(stuff_cb);
function stuff_cb(results) {
var transformed = trans(results);
db.stuffWith(transformed, stuffWith_cb);
}
function stuffWith_cb(endResult) {
var finalTransform = final(endResult, /* where is my transformed? */);
response.end(finalTransform);
}
}
// and ugly solution (well, it only really gets ugly after several of these.
function myfunction(response) {
db.stuff(stuff_cb);
var transformed;
function stuff_cb(results) {
transformed = trans(results);
db.stuffWith(transformed, stuffWith_cb);
}
function stuffWith_cb(endResult) {
var finalTransform = final(endResult, transformed);
response.end(finalTransform);
}
}
// Lets try async.waterfall. Since its meant for deep nesting, lets add some serious nesting:
function myfunction(response) {
db.stuff1(function stuff1_cb(result1) {
var transformed1 = trans1(result1);
db.stuff2(transformed1, function stuff2_cb(result2) {
var transformed2 = trans2(result2);
db.stuff3(transformed2, function stuff3_cb(result3) {
var transformed3 = trans3(result3);
db.stuff4(transformed3, function stuff4_cb(result4) {
var transformed4 = trans4(result3);
db.stuff4(transformed4, function stuff5_cb(result5) {
var finalTransform = final(result5, result2, transformed2, transformed1);
response.end(finalTransform);
});
});
});
});
});
}
// Solution #2: async.waterfall. Since waterfall is really meant for deeper nesting...
// lets make the original real deep.
function myfunction(response) {
db.stuff1(function stuff1_cb(result1) {
var transformed1 = trans1(result1);
db.stuff2(transformed1, function stuff2_cb(result2) {
var transformed2 = trans2(result2);
db.stuff3(transformed2, function stuff3_cb(result3) {
var transformed3 = trans3(result3);
db.stuff4(transformed3, function stuff4_cb(result4) {
var transformed4 = trans4(result4);
db.stuff5(transformed4, function stuff5_cb(result5) {
var finalTransform = final(result5, result2, transformed2, transformed1);
response.end(finalTransform);
});
});
});
});
});
}
// And here it is in async.waterfall.
function myfunction(response) {
async.waterfall([
function (callback) {
db.stuff1(function (result1) {
var transformed1 = trans1(result1);
callback(null, transformed1);
});
},
function (transformed1, callback) {
db.stuff2(transformed1, function (result2) {
var transformed2 = trans2(result2);
callback(null, transformed2);
})
},
function (transformed2, callback) {
db.stuff3(transformed2, function (result3) {
var transformed3 = trans3(result3);
callback(null, transformed3);
})
},
function (transformed3, callback) {
db.stuff4(transformed3, function (result4) {
var transformed4 = trans4(result4);
callback(null, transformed4);
})
}], function (err, transformed4) {
db.stuff5(transformed4, function (result5) {
var finalTransform = final(result5, /* where is my result 2 */ ,
/* where is my transformed2 */ , /* where is my transformed1 */ );
response.end(finalTransform);
})
});
}
// Oops, we need to pass those other variables too. No more free-ride closures.
function myfunction(response) {
async.waterfall([
function (callback) {
db.stuff1(function (result1) {
var transformed1 = trans1(result1);
callback(null, transformed1);
});
},
function (transformed1, callback) {
db.stuff2(transformed1, function (result2) {
var transformed2 = trans2(result2);
callback(null, result2, transformed1, transformed2);
})
},
function (result2, transformed1, transformed2, callback) {
db.stuff3(transformed2, function (result3) {
var transformed3 = trans3(result3);
callback(null, result2, transformed1, transformed2, transformed3);
})
},
function (result2, transformed1, transformed2, transformed3, callback) {
db.stuff4(transformed3, function (result4) {
var transformed4 = trans4(result4);
callback(null, result2, transformed1, transformed2, transformed4);
})
}], function (err, result2, transformed1, transformed2, transformed4) {
db.stuff5(transformed4, function (result5) {
var finalTransform = final(result5, result2, transformed2, transformed1);
response.end(finalTransform);
})
});
}
// I had to pass some of them 5 times. So at the end, we really are back at square one: defining them in the outer scope.
function myfunction(response) {
var transformed1, result2_outer, transformed2;
async.waterfall([
function (callback) {
db.stuff1(function (result1) {
transformed1 = trans1(result1);
callback(null, transformed1);
});
},
function (transformed1, callback) {
db.stuff2(transformed1, function (result2) {
result2_outer = result2;
transformed2 = trans2(result2);
callback(null, transformed2);
})
},
function (transformed2, callback) {
db.stuff3(transformed2, function (result3) {
var transformed3 = trans3(result3);
callback(null, transformed3);
})
},
function (transformed3, callback) {
db.stuff4(transformed3, function (result4) {
var transformed4 = trans4(result4);
callback(null, transformed4);
})
}], function (err, transformed4) {
db.stuff5(transformed4, function (result5) {
var finalTransform = final(result5, result2_outer, transformed2, transformed1);
response.end(finalTransform);
})
});
}
// Naming your callbacks to keep nesting shallow, problem #2
function myfunction(response) {
// ...
// other code
// ...
db.stuff(stuff_cb);
function stuff_cb(results) {
var transformed = trans(results);
db.stuffWith(transformed, stuffWith_cb);
}
// LOL MY CAT ADDED CODE HERE because it wasn't obvius
// that these functions flow one after another.
// And now its even less obvious.
function stuffWith_cb(endResult) {
var finalTransform = final(endResult, /* where is my transformed? */);
response.end(finalTransform);
}
}
// problem #2 solution: none except discipline and being careful not to break flows.
// conclusion: nesting is okay. a couple of tabs are comparatively a small price to pay.
@bmeck
Copy link

bmeck commented Sep 18, 2012

function myfunction(response) {
async.waterfall([
transform.bind(db, 'stuff1', trans1)
transform.bind(db, 'stuff2', trans2)
transform.bind(db, 'stuff3', trans3)
transform.bind(db, 'stuff4', trans4)
], function (data1, x1, data2, x2, data3, x3, data4, x4) {
db.stuff4(x4, function stuff5_cb(data5) {
response.end(final(data5, data2, x2, x1));
});
});
}

//
// Doing same darn thing over and over when nesting, helper describes this atrocity
//

function transform(name, xform) {
var args = [].slice.call(arguments, 2);
var fn = args.pop();
var self = this;
function finish(result) {
return fn.apply(self, [null].concat(args).concat([result, xform(result)]));
}
if (args.length) {
var data = args.pop();
return this[name](data, finish);
}
else {
return this[name](data, finish);
}
}

@bmeck
Copy link

bmeck commented Sep 18, 2012

function myfunction(response) {
  async.waterfall([
    transform.bind(db, 'stuff1', trans1)
    transform.bind(db, 'stuff2', trans2)
    transform.bind(db, 'stuff3', trans3)
    transform.bind(db, 'stuff4', trans4)
  ], function (data1, x1, data2, x2, data3, x3, data4, x4) {
    db.stuff4(x4, function stuff5_cb(data5) {
      response.end(final(data5, data2, x2, x1));
    });
  });
}

function transform(name, xform) {
  var args = [].slice.call(arguments, 2);
  var fn = args.pop();
  var self = this;
  function finish(result) {
    return fn.apply(self, [null].concat(args).concat([result, xform(result)]));
  }
  if (args.length) {
    var data = args.pop();
    return this[name](data, finish);
  }
  else {
    return this[name](data, finish);
  }
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment