Skip to content

Instantly share code, notes, and snippets.

@nullstyle
Last active August 29, 2015 14:01
Show Gist options
  • Save nullstyle/53ae6c350d9c87c4ae56 to your computer and use it in GitHub Desktop.
Save nullstyle/53ae6c350d9c87c4ae56 to your computer and use it in GitHub Desktop.
An example of how Livescript can mix sync/async code in an IMO more elegant way

Livescript and the left-pointing arrow

An example of something that livescript brings to the table above and beyond javascript I'd like to show you the <- operator.

As explained in the docs, Backcalls let you "unnest" callbacks, making async code more readable. For example:

#async node.js
err, data <- fs.read-file "1.txt"
console.log data

#the sync equivalent (note, no err because the function will throw if one occurs)
data = fs.read-file-sync "1.txt"
console.log data

Notice that the two look almost equivalent. Translated to JS, the async form looks like:

fs.readFile("1.txt", function(err, data) {
  console.log(data);
})

Simple enough. Let's look at a situation with two backcalls.

cache-exists <- fs.exists "1.txt"
if cache-exists
  err, data <- fs.read-file "1.txt"
  console.log(data)
else
  console.log "1.txt not found"

becomes:

fs.exists("1.txt", function(cacheExists){
  if (cacheExists) {
    fs.readFile("1.txt", function(err, data){
      console.log(data);
    });
  } else {
    console.log("1.txt not found");
  }
});

Notice that the async machinery doesn't introduce further nesting, keeping indentations down and readability up.

A real world example.

I find myself processing OpenStreetMap data using node.js and osmium. I'm dealing with ~30 gigabytes of encoded information that needs to be sifted through to answer the queries I have. Each test run (dealing with only a 2 gb subset of the data at present) takes roughly 60 seconds. To speed up my iteration I've implemented a file-based caching system that I can use to gain some efficiency from stage to stage as I develop my app.

Notice in the file-cached function (util.ls) that I mix async/sync code rather naturally... the only difference being whether I use = or <-.

require! "./lib/util".file-cached
err, results <- file-cached "1.json", (cb) ->
console.log("populating... some slow function here")
cb(null, [1,2,3])
console.log(err) && process.exit 1 if err?
console.log results
require! fs
export function file-cached(path, populator, cb)
cache-exists <- fs.exists path
if cache-exists
err, data <- fs.read-file path, encoding: "utf-8"
return cb(err, null) if err?
try
results = JSON.parse(data)
cb(null, results)
catch
cb(e, null)
else
err, results <- populator!
return cb(err, null) if err?
data = JSON.stringify(results, undefined, 2)
err <- fs.write-file path, data
return cb(err, null) if err?
cb(null, results)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment