-
-
Save creationix/468889 to your computer and use it in GitHub Desktop.
var compiler = require('./asyncCompiler'), | |
fs = require('fs'); | |
compiler.helpers = { | |
partial: function (filename, data, callback) { | |
fs.readFile(filename, function (err, text) { | |
if (err) { callback(err); return; } | |
compiler(text, data, callback); | |
}); | |
} | |
} | |
var template = "Hello @planet, my name is @name() and I am @age() years old."; | |
var data = { | |
// Value | |
planet: "world", | |
// Async helper | |
name: function name(callback) { | |
process.nextTick(function () { | |
callback(null, "Tim Caswell"); | |
}); | |
}, | |
// Sync helper | |
age: function age() { | |
return 28; | |
} | |
}; | |
// Get the compiled template, and the execute it | |
var fn = compiler(template); | |
console.log("\nJavaScript"); | |
console.log(fn.toString()); | |
fn(data, function (err, text) { | |
console.log("OUTPUT " + text); | |
// Or do it in one shot | |
compiler(template, data, function (err, text) { | |
console.log("OUTPUT2 " + text); | |
// Or use a stream | |
var stream = new process.EventEmitter(); | |
fn(data, stream); | |
stream.addListener('data', function (data) { | |
console.log("DATA " + JSON.stringify(data.toString())); | |
}); | |
stream.addListener('end', function () { | |
console.log("END"); | |
}); | |
stream.addListener('error', function (err) { | |
console.log("ERROR " + err.stack); | |
}); | |
}); | |
}); |
JavaScript | |
function fn(locals, callback) { | |
locals.__proto__ = module.exports.helpers; | |
var position = 0, stream, chunks; | |
if (!(typeof callback === 'function')) { | |
if (callback instanceof process.EventEmitter) { | |
stream = callback; | |
callback = undefined; | |
} else { | |
throw new Error("Type Error: last argument must be either a callback function or stream instance"); | |
} | |
} | |
function check() { | |
var pieces = []; | |
while (chunks[position] && position < chunks.length) { | |
if (stream) { | |
pieces.push(chunks[position]); | |
} | |
position++; | |
} | |
if (stream && pieces.length > 0) { | |
stream.emit('data', new Buffer(pieces.join(""))); | |
} | |
if (position === chunks.length) { | |
if (callback) { | |
callback(null, new Buffer(chunks.join(""))); | |
} | |
if (stream) { | |
stream.emit('end'); | |
} | |
} | |
} | |
function execute(position, fn) { | |
try { | |
chunks[position] = fn(function (err, result) { | |
if (err) { | |
if (stream) stream.emit('error', err); | |
if (callback) callback(err); | |
return; | |
} | |
chunks[position] = result; | |
check(); | |
}); | |
} catch (err) { | |
if (stream) stream.emit('error', err); | |
if (callback) callback(err); | |
} | |
} | |
process.nextTick(function () { | |
with(locals) { | |
// START GENERATED CODE | |
chunks = new Array(5); | |
chunks[0] = "Hello " + planet + ", my name is "; | |
execute(1, name); | |
chunks[2] = " and I am "; | |
execute(3, age); | |
chunks[4] = " years old"; | |
check(); | |
// END GENERATED CODE | |
} | |
}); | |
} | |
OUTPUT Hello world, my name is Tim Caswell and I am 28 years old | |
OUTPUT2 Hello world, my name is Tim Caswell and I am 28 years old | |
DATA "Hello world, my name is " | |
DATA "Tim Caswell and I am 28 years old" | |
END |
there are tons of use cases for async views, especially in node (think partials) where all I/O is required to be async.
Also, I'm working on a streaming version that emits chunks as they are ready. This is great for landing pages where you want a fast initial load, but some data takes a while to compute.
http://github.com/raycmorgan/Mu supports async parsing/compiling and is based on Mustache
its easy to do partials without async, IMO (unless your reloading) you should not need to do any IO for views. I benchmarked Mu vs haml.js and they are nearly identical. Im not against the idea, I just dont think it has much relevance for 99% of views
the chunking thing would be nice in some cases, but most user agents I have seen dont even output the chunks as they receive them, at least most browsers cant render
if you wanted that though I suppose even with existing stuff like haml or jade, you could change the generated buf.push() calls to res.write(). dont get me wrong though im totally not against the idea, but until I have a practical need for it myself it is not worth implementing. I can see this being more useful for maybe feed generation or something along those lines where you could pipe the data through
Updated API per bradleymeck's suggestions on IRC
--pending || callback(null, chunks.join('')); is the idiom I use, nice and simple. I dont see much point for many cases with async views though. You view waits on pending data anyways so it is not much different than pending before passing some locals to a view