Skip to content

Instantly share code, notes, and snippets.

@narqo
Created January 12, 2014 13:14
Show Gist options
  • Save narqo/8384437 to your computer and use it in GitHub Desktop.
Save narqo/8384437 to your computer and use it in GitHub Desktop.
Comparison of different ways to implement `fs#scan()`
var fs = require('fs'),
path = require('path');
/**
* Async version of `scan`
*
* @example
* var scan = require('./scan-async');
* scan('<dir>', function(err, files) {
* // do something with `files` object
* });
*
* @param {String} dir
* @param {Function} cb
*/
module.exports = function scan(dir, cb) {
dir = path.resolve(dir);
var flat = [],
files = {
flat : flat
};
walk(dir, function(err) {
if(err) return cb(err);
cb(null, files);
});
function push(item) {
flat.push({ file: item.fullpath });
}
function walk(dir, cb) {
_walk(dir, function(f, next) {
if(f.stat.isDirectory()) {
walk(f.fullpath, next);
return;
}
if(f.stat.isFile()) {
push(f);
}
next();
}, cb);
}
};
function _walk(dir, onFile, done) {
fs.readdir(dir, traverse);
function traverse(err, files) {
if(err) {
done(err);
return;
}
var pending = files.length;
if(pending === 0) {
done();
return;
}
files.forEach(function(f) {
if(f[0] === '.') {
next();
return;
}
var p = dir + '/' + f;
onFile({
dir : dir,
file : f,
fullpath : p,
stat : fs.statSync(p)
}, next);
});
function next(err) {
--pending === 0 && done(err);
}
}
}
var fs = require('fs'),
path = require('path'),
through = require('through');
/**
* Stream version of `scan`
*
* @example
* var scan = require('./scan-stream');
* var concat = require('concat-stream');
* scan('<dir>').pipe(concat(function(files) {
* // do something with `files` object
* });
*
* @param {String} dir
* @returns {Stream} ReadableWritable (through) stream
*/
module.exports = function scan(dir) {
dir = path.resolve(dir);
var flat = [],
files = {
flat : flat
},
tr = through(),
pending = 0;
walk(dir);
return tr.pipe(through(write, end));
function push(item) {
tr.queue({ file: item.fullpath });
}
function write(item) {
flat.push(item);
}
function end() {
this.queue(files);
this.queue(null);
}
function walk(dir) {
++pending;
var t = through(function write(f) {
if(f.stat.isDirectory()) {
walk(f.fullpath).pipe(t);
return;
}
if(f.stat.isFile()) {
push(f);
}
}, function() {
this.queue(null);
if(--pending === 0) tr.queue(null);
});
return _walk(dir).pipe(t);
}
};
function _walk(dir) {
var tr = through();
fs.readdir(dir, function(err, files) {
if(err) {
tr.emit('error', err);
return;
}
files.forEach(function(f) {
if(f[0] === '.') return;
var p = dir + '/' + f;
tr.queue({
dir : dir,
file : f,
fullpath : p,
stat : fs.statSync(p)
});
});
tr.queue(null);
});
return tr;
}
var fs = require('fs'),
path = require('path');
/**
* Sync version of `scan`
*
* @example
* var scan = require('./scan-sync');
* var files = scan('<dir>');
*
* @param {String} dir
* @returns {Object}
*/
module.exports = function scan(dir) {
dir = path.resolve(dir);
var flat = [],
files = {
flat : flat
};
walk(dir);
return files;
function push(item) {
flat.push({ file: item.fullpath });
}
function walk(dir) {
var files = _walk(dir);
files.forEach(function(f) {
if(f.stat.isDirectory()) {
walk(f.fullpath);
return;
}
if(f.stat.isFile()) {
push(f);
}
});
}
};
function _walk(dir) {
var files = fs.readdirSync(dir);
if(files.length === 0) {
return [];
}
return files.reduce(function(files, f) {
if(f[0] === '.') return files;
var p = dir + '/' + f;
files.push({
dir : dir,
file : f,
fullpath : p,
stat : fs.statSync(p)
});
return files;
}, []);
}
@narqo
Copy link
Author

narqo commented Jan 12, 2014

Some silly benchmarks:

  › tree blocks/
blocks/
├── ecma
│   ├── __array
│   │   ├── ecma__array.js
│   │   └── ecma__array.spec.js
│   ├── __function
│   │   ├── ecma__function.js
│   │   └── ecma__function.spec.js
│   ├── __json
│   │   ├── ecma__json.js
│   │   └── ecma__json.spec.js
│   ├── __object
│   │   ├── ecma__object.js
│   │   └── ecma__object.spec.js
│   ├── __string
│   │   ├── ecma__string.js
│   │   └── ecma__string.spec.js
│   ├── ecma.ru.title.txt
│   └── ecma.ru.wiki
├── idle
│   ├── _start
│   │   └── idle_start_auto.js
│   ├── idle.deps.js
│   └── idle.js
├── jquery
│   ├── __config
│   │   └── jquery__config.js
│   ├── __event
│   │   └── _type
│   │       ├── jquery__event_type_pointer.deps.js
│   │       ├── jquery__event_type_pointer.js
│   │       └── jquery__event_type_pointer.ru.title.txt
│   ├── jquery.js
│   └── jquery.ru.title.txt
└── loader
    └── _type
        └── loader_type_js.js

Making 1000 scans:

  › node stream.js
scan stream./blocks: 1391ms

  › node sync.js
scan sync ./blocks: 829ms

  › node async.js
scan async./blocks: 699ms

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