从多级JSON(使用async.js)recursion创build目录

定义

File#createDirectoriesFromJSON (json, cb); 

json:JSON对象。

cb:function。 参数:error(Error),created(布尔值,如果至less有一个目录已经创build,则为true)。

假设File类包含一个名为_path的属性,其中包含一个目录的path。

用法

 var json = { b: { c: { d: {}, e: {} }, f: {} }, g: { h: {} } }; //this._path = "." new File (".").createDirectoriesFromJSON (json, function (error, created){ console.log (created); //Prints: true //callback binded to the File instance (to "this"). Hint: cb = cb.bind (this) this.createDirectoriesFromJSON (json, function (error, created){ console.log (created); //Prints: false (no directory has been created) }); }); 

结果

在“。”下 json对象中显示的目录树已创build。

./b/c/d
./b/c/e
./b/f
./b/g/h

实施

这是我没有 async.js:

 File.prototype.createDirectoriesFromJSON = function (json, cb){ cb = cb.bind (this); var created = false; var exit = false; var mkdir = function (path, currentJson, callback){ var keys = Object.keys (currentJson); var len = keys.length; var done = 0; if (len === 0) return callback (null); for (var i=0; i<len; i++){ (function (key, i){ var dir = PATH.join (path, key); FS.mkdir (dir, function (mkdirError){ exit = len - 1 === i; if (mkdirError && mkdirError.code !== "EEXIST"){ callback (mkdirError); return; }else if (!mkdirError){ created = true; } mkdir (dir, currentJson[key], function (error){ if (error) return callback (error); done++; if (done === len){ callback (null); } }); }); })(keys[i], i); } }; var errors = []; mkdir (this._path, json, function (error){ if (error) errors.push (error); if (exit){ errors = errors.length === 0 ? null : errors; cb (errors, errors ? false : created); } }); }; 

只是为了好奇,我想用async.js来重写函数。 这里的问题是函数是recursion和并行的。 例如,“b”文件夹与“g”并行创build。 “b / c”和“b / f”,“b / c / d”和“b / c / e”也是一样。

我的尝试:

 var _path = require('path'); var _fs = require('fs'); var _async = require('async'); function File() { this._path = __dirname + '/test'; } File.prototype.createDirectoriesFromJSON = function(json, cb) { var created = [], errors = []; function iterator(path, currentJson, key, fn){ var dir = _path.join(path, key); _fs.mkdir(dir, function(mkdirError) { if(mkdirError && mkdirError.code !== "EEXIST") { errors.push(mkdirError); } else if(!mkdirError) { created.push(dir); } mkdir(dir, currentJson[key], fn); }); } function mkdir(path, currentJson, callback) { var keys = Object.keys(currentJson); if(keys.length === 0) return callback(null); _async.forEach(keys, iterator.bind(this, path, currentJson), callback); } mkdir(this._path, json, cb.bind(this, errors, created)); }; new File().createDirectoriesFromJSON({ b: { c: { d: {}, e: {} }, f: {} }, g: { h: {} } }, function(errors, successes) { // errors is an array of errors // successes is an array of successful directory creation console.log.apply(console, arguments); }); 

经testing:

 $ rm -rf test/* && node test.js && tree test [] [ '/Users/fg/Desktop/test/b', '/Users/fg/Desktop/test/g', '/Users/fg/Desktop/test/b/c', '/Users/fg/Desktop/test/b/f', '/Users/fg/Desktop/test/g/h', '/Users/fg/Desktop/test/b/c/d', '/Users/fg/Desktop/test/b/c/e' ] null test |-- b | |-- c | | |-- d | | `-- e | `-- f `-- g `-- h 7 directories, 0 files 

笔记:

  • 由于errors.push(mkdirError); 意味着该目录无法创build, return fn(null); 可以附加到它停止从这个分支创build的目录。
  • cb将会收到第三个参数,它总是为null
  • 我最好使用扳手 .mkdirSyncRecursive()这种任务,或者使用asynchronousmkdirp 。

[更新]使用mkdirp和lodash(或下划线)代码可以更清晰:

 var _path = require('path'); var _fs = require('fs'); var _async = require('async'); var _mkdirp = require('mkdirp'); var _ = require('lodash'); // or underscore function File() { this._path = __dirname + '/test'; } File.prototype.flattenJSON = function(json){ function walk(path, o, dir){ var subDirs = Object.keys(o[dir]); path += '/' + dir; if(subDirs.length === 0){ return path; } return subDirs.map(walk.bind(null, path, o[dir])); } return _.flatten(Object.keys(json).map(walk.bind(null, this._path, json))); }; File.prototype.createDirectoriesFromJSON = function(json, cb) { var paths = this.flattenJSON(json) , created = [] , errors = []; function iterator(path, fn){ _mkdirp(path, function(mkdirError) { if(mkdirError && mkdirError.code !== "EEXIST") { errors.push(mkdirError); } else if(!mkdirError) { created.push(path); } return fn(null); }); } _async.forEach(paths, iterator, cb.bind(this, errors, created)); }; new File().createDirectoriesFromJSON({ b: { c: { d: {}, e: {} }, f: {} }, g: { h: {} } }, function(errors, successes) { // errors is an array of error // successes is an array of successful directory creation console.log.apply(console, arguments); }); 

经testing:

 $ rm -rf test/* && node test2.js && tree test [] [ '/Users/fg/Desktop/test/b/f', '/Users/fg/Desktop/test/g/h', '/Users/fg/Desktop/test/b/c/d', '/Users/fg/Desktop/test/b/c/e' ] null test |-- b | |-- c | | |-- d | | `-- e | `-- f `-- g `-- h 7 directories, 0 files 

注意:

  • iterator可以通过使用部分函数应用程序,但下划线/ lodash只支持部分从左到右,因此我不想要求另一个库这样做。

不知道这是否是最好的解决scheme,但我已经通过创build一个额外的“监视器”types的对象解决了这个问题。 简而言之,将您的初始化更改为如下所示:

 var monitor = { var init = function(json, cb) { this.outerDirLength = Object.keys (currentJson); this.processedOuterDirs = 0; //track 'report progress' calls this.completedOuterDirs = 0; //'report progress' calls with no errors = success this.errors = []; this.finishedCallback = cb; } var reportProgress = function(error) { this.processedOuterDirs++; if (error) this.errors.push(error); else this.completedOuterDirs++; if (this.isComplete()) this.finish(); } var finish = function () { var errors = this.errors.length === 0 ? null : this.errors; this.finishedCallback(errors, errors ? false : this.completedOuterDirs); } var isComplete = function() { return this.processedOuterDirs == this.outerDirLength; } }; monitor.init(json, cb); if (monitor.isComplete()) { //handle case of JSON with zero definitions monitor.finish(); return; } mkdir (this._path, json, function (error){ monitor.reportProgress(error); }); 

请注意,上面还没有经过testing(甚至是testing编译),但应该给你的想法…如果你要使mkdir真正asynchronous,你可能会改变上面,以便在每次调用mkdir它计算了要创build多less个目录,并在监视器上增加了预期的目标,然后更新了每个创build的监视器。