文件加载的竞争条件

时间:2016-02-01 09:19:25

标签: node.js race-condition

如果我通过命令行使用node并需要模块,请使用load函数,然后使用get函数从配置文件返回预期的字符串。

➜  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"
}

1 个答案:

答案 0 :(得分:3)

您的load函数是异步的。这意味着它启动操作并立即返回,然后实际操作在一段时间后完成。您需要将界面更改为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()函数的内容中,并且您的代码都没有看到该异常。这就是我使用回调和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];
    }
};