文件加载的竞争条件
Race conditions with file loading
如果我通过命令行使用 node 并需要模块,请使用 load
函数,然后使用 get
函数 returns 配置文件中的预期字符串。
➜ cz node
> var config = require('./index.js');
undefined
> config.load('/Users/xo/code/cz/config.json');
undefined
> config.get()
{ username: 'xo' }
> config.get('username')
'xo'
如果我通过文件尝试相同的操作,我会返回 undefined
而不是 { username: xo }
。
➜ cz node test.js
undefined
这是 test.js
文件。
var config = require('./index.js');
config.load('./config.json');
console.log(config.get('username'));
这是我的模块。
'use strict';
const fs = require('fs');
let config = {};
module.exports = {
load: function(path) {
fs.readFile(path, function(err, data) {
if (err) { throw err; }
data = JSON.parse(data);
for(var prop in data){
if (data.hasOwnProperty(prop)) {
config[prop] = data[prop];
}
}
});
},
get: function(prop){
if(prop){
return config[prop];
} else {
return config;
}
},
set: function(prop, value) {
config[prop] = value;
return config[prop];
}
};
这是 config.json
文件。
{
"username": "xo"
}
您的 load
函数是异步的。这意味着它会立即开始操作并 returns ,然后实际操作会在一段时间后完成。您将需要更改 load
函数的接口,以便调用者可以知道它何时完成以及何时可以安全地进行其他操作。
这里有很多可能的设计。一种方式是这样的:
'use strict';
const fs = require('fs');
let config = {};
module.exports = {
load: function(path, callback) {
fs.readFile(path, function(err, data) {
if (err) { return callback(err);}
data = JSON.parse(data);
for(var prop in data){
if (data.hasOwnProperty(prop)) {
config[prop] = data[prop];
}
}
// communicate that all data is now loaded
callback(null);
});
},
get: function(prop){
if(prop){
return config[prop];
} else {
return config;
}
},
set: function(prop, value) {
config[prop] = value;
return config[prop];
}
};
然后,您的调用方可以知道加载操作何时完成:
var config = require('./index.js');
config.load('./config.json', function(err) {
// in here, we know that the config data is done loading
if (err) {
console.log(err);
} else {
console.log(config.get('username'));
}
});
此外,请意识到在异步回调中执行 throw err
对您没有好处。该异常只是返回到 fs.readFile()
函数的内部,并且您的代码的 none 曾经看到该异常。这就是我使用回调和 node.js 回调调用约定来传达错误的原因。
此外,您可能想知道 require()
函数(如果给定一个以 .json
结尾的文件名)将自动为您解析 JSON 并加载它同步(它是为了在启动时加载配置信息时使用)。
所以,你也可以这样做:
let config = require('/Users/xo/code/cz/config.json');
require()
将同步加载数据(类似于 fs.readFileSync()
),因此您不必使用回调方案。
如果您的代码使得配置可以在启动时同步加载,那么您可以这样做:
'use strict';
const fs = require('fs');
module.exports = function(path) {
let config = require(path);
return {
get: function(prop) {
if (prop) {
return config[prop];
} else {
return config;
}
},
set: function(prop, value) {
config[prop] = value;
return config[prop];
}
}
}
然后,它的用法将如下所示:
var config = require('./index.js')('./config.json');
console.log(config.get('username'));
您也可以像这样简化 load()
方法:
'use strict';
const fs = require('fs');
let config = {};
module.exports = {
load: function(path) {
Object.assign(config, require(path));
},
get: function(prop){
if(prop){
return config[prop];
} else {
return config;
}
},
set: function(prop, value) {
config[prop] = value;
return config[prop];
}
};
如果我通过命令行使用 node 并需要模块,请使用 load
函数,然后使用 get
函数 returns 配置文件中的预期字符串。
➜ cz node
> var config = require('./index.js');
undefined
> config.load('/Users/xo/code/cz/config.json');
undefined
> config.get()
{ username: 'xo' }
> config.get('username')
'xo'
如果我通过文件尝试相同的操作,我会返回 undefined
而不是 { username: xo }
。
➜ cz node test.js
undefined
这是 test.js
文件。
var config = require('./index.js');
config.load('./config.json');
console.log(config.get('username'));
这是我的模块。
'use strict';
const fs = require('fs');
let config = {};
module.exports = {
load: function(path) {
fs.readFile(path, function(err, data) {
if (err) { throw err; }
data = JSON.parse(data);
for(var prop in data){
if (data.hasOwnProperty(prop)) {
config[prop] = data[prop];
}
}
});
},
get: function(prop){
if(prop){
return config[prop];
} else {
return config;
}
},
set: function(prop, value) {
config[prop] = value;
return config[prop];
}
};
这是 config.json
文件。
{
"username": "xo"
}
您的 load
函数是异步的。这意味着它会立即开始操作并 returns ,然后实际操作会在一段时间后完成。您将需要更改 load
函数的接口,以便调用者可以知道它何时完成以及何时可以安全地进行其他操作。
这里有很多可能的设计。一种方式是这样的:
'use strict';
const fs = require('fs');
let config = {};
module.exports = {
load: function(path, callback) {
fs.readFile(path, function(err, data) {
if (err) { return callback(err);}
data = JSON.parse(data);
for(var prop in data){
if (data.hasOwnProperty(prop)) {
config[prop] = data[prop];
}
}
// communicate that all data is now loaded
callback(null);
});
},
get: function(prop){
if(prop){
return config[prop];
} else {
return config;
}
},
set: function(prop, value) {
config[prop] = value;
return config[prop];
}
};
然后,您的调用方可以知道加载操作何时完成:
var config = require('./index.js');
config.load('./config.json', function(err) {
// in here, we know that the config data is done loading
if (err) {
console.log(err);
} else {
console.log(config.get('username'));
}
});
此外,请意识到在异步回调中执行 throw err
对您没有好处。该异常只是返回到 fs.readFile()
函数的内部,并且您的代码的 none 曾经看到该异常。这就是我使用回调和 node.js 回调调用约定来传达错误的原因。
此外,您可能想知道 require()
函数(如果给定一个以 .json
结尾的文件名)将自动为您解析 JSON 并加载它同步(它是为了在启动时加载配置信息时使用)。
所以,你也可以这样做:
let config = require('/Users/xo/code/cz/config.json');
require()
将同步加载数据(类似于 fs.readFileSync()
),因此您不必使用回调方案。
如果您的代码使得配置可以在启动时同步加载,那么您可以这样做:
'use strict';
const fs = require('fs');
module.exports = function(path) {
let config = require(path);
return {
get: function(prop) {
if (prop) {
return config[prop];
} else {
return config;
}
},
set: function(prop, value) {
config[prop] = value;
return config[prop];
}
}
}
然后,它的用法将如下所示:
var config = require('./index.js')('./config.json');
console.log(config.get('username'));
您也可以像这样简化 load()
方法:
'use strict';
const fs = require('fs');
let config = {};
module.exports = {
load: function(path) {
Object.assign(config, require(path));
},
get: function(prop){
if(prop){
return config[prop];
} else {
return config;
}
},
set: function(prop, value) {
config[prop] = value;
return config[prop];
}
};