Javascript 从多级JSON(使用async.js)递归创建目录
定义Javascript 从多级JSON(使用async.js)递归创建目录,javascript,node.js,asynchronous,async.js,Javascript,Node.js,Asynchronous,Async.js,定义 File#createDirectoriesFromJSON (json, cb); json:json对象 cb:功能。参数:error(error),created(布尔值,如果至少创建了一个目录,则为true) 请确保File类包含名为\u path的属性,该属性包含目录的路径 用法 var json = { b: { c: { d: {}, e: {} }, f: {}
File#createDirectoriesFromJSON (json, cb);
json:json对象
cb:功能。参数:error(error),created(布尔值,如果至少创建了一个目录,则为true)
请确保File类包含名为\u 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对象中显示的目录树 /b/c/d
/b/c/e
/b/f
/b/g/h
实施
File#createDirectoriesFromJSON (json, cb);
这就是我在没有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);
}
});
};
File.prototype.createDirectoriesFromJSON=函数(json,cb){
cb=cb.bind(本);
var=false;
var exit=false;
var mkdir=函数(路径、currentJson、回调){
var keys=Object.keys(currentJson);
var len=keys.length;
var done=0;
if(len==0)返回回调(null);
对于(var i=0;i我不知道这是否是最好的解决方案,但我在过去通过创建一个额外的“monitor”类型对象解决了这个问题。简言之,将您的初始化更改为:
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);
});
请注意,以上内容尚未测试(甚至未编译测试)但是应该给你一个想法……如果你要使mkdir真正异步,你可能会改变上面的内容,这样在每次调用mkdir时,它都会计算要创建多少dir,并增加监视器上的预期目标,然后在创建每个dir时更新监视器。不知道这是否是最好的解决方案,但我已经知道了过去通过创建一个额外的“监视器”类型对象解决了这一问题。简言之,将您的初始化更改为:
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);
});
请注意,以上内容尚未测试(甚至未编译测试)但是应该给你一个想法……如果你要使mkdir真正异步,你可能会改变上述内容,以便在每次调用mkdir时,它计算要创建多少dir,并增加监视器上的预期目标,然后在创建每个dir时更新监视器。我的尝试:
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);
});
通过以下方式进行测试:
$ 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);
意味着无法创建目录,因此可以将return fn(null);
附加到它以停止从此分支创建目录
cb
将收到第三个参数,该参数始终为null
- 对于此类任务,我最好使用
.mkdirSyncRecursive()
,或者使用substack async
[更新]使用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);
});
通过以下方式进行测试:
$ 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
注:
迭代器
可以通过使用分部函数应用程序删除,但是下划线/lodash只支持从左到右的分部,因此我不想要求另一个库这样做
我的尝试:
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);
});
通过以下方式进行测试:
$ 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);
意味着无法创建目录,因此可以将return fn(null);
附加到它以停止从此分支创建目录
cb
将收到第三个参数,该参数始终为null
- 对于此类任务,我最好使用
.mkdirSyncRecursive()
,或者使用substack async
[更新]使用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);
});
通过以下方式进行测试:
$ 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
注:
迭代器
可以通过使用分部函数应用程序删除,但是下划线/lodash只支持从左到右的分部,因此我不想要求另一个库这样做
Wow!非常好的例子。我也想到了第二种方法,但是它比第一种方法做了更多的循环,因为它必须计算完整的路径。在错误之后。push(mkdirError);
不应该是返回值
?如果你谈论第二种方法,那么不会,因为它会阻止async.forEach(调用的第一次迭代)带有错误的“fn”将立即停止forEach并调用最终回调)哇!非常好的示例。我也认为第二种方法比第一种方法执行更多的循环,因为它必须计算完整路径。Aftererrors.push(mkdirError);
不应该是返回值
?如果您谈论第二个示例,则不应该,因为它会阻止async.forEach(第一次调用“fn”并出现错误的迭代将立即停止forEach并调用最终回调)