async alternative to nodejs require, if opts.watch===true resolve module cache on file change
@param {String} filePath path
@param {Object} opts
@param {Function} callback (err, module)
function requireAsync(filePath, opts, cb) {
    if(arguments.length === 2){
        cb = arguments[1];
        opts = {};
    }
    filePath = path.resolve(filePath);
    opts.encoding = opts.encoding || 'utf8';
    // opts.watch - if true, watcher will be set
    // opts.watchPersistent - watcher opts
    // opts.isJson - if true, use json parse, instead of eval
    // opts.jsonParse - custom json parse function
    
    // set watch, if requireWatch is run first time
    if(!_fileWatchers[filePath] && opts.watch) {
        try {
            _fileWatchers[filePath] = fs.watch(filePath, { persistent: opts.watchPersistent || false })
            .on('change', function(){
                // delete require.cache[require.resolve(filePath)];
                delete _modules[filePath];
            }).on('error', function(){
                this.close();
                _fileWatchers[filePath] = null;
            });
        }
        catch(err){
            return cb(new Error('fsExt.requireAsync: setup file watch failed').details({ filePath:filePath, cause:err }));
        }
    }
    
    if(!_modules[filePath]){
        fs.readFile(filePath, { encoding: opts.encoding }, function(err, data) {
            var loadingModule = {
                exports: {},
                module:{ exports:{} }
            };
            
            if(err) return cb(new Error('fsExt.requireAsync: reading file failed').details({ filePath:filePath, cause:err }));
            else if(opts.isJson){
                try {
                    loadingModule.exports = JSON.parse(data, opts.jsonParse);
                }
                catch(err){
                    return cb(new Error('fsExt.requireAsync: parsing json data failed').details({ filePath:filePath, cause:err }));
                }
            }
            else {
                var code = '(function (module) {' + data + '})(loadingModule);';
                try {
                    eval(code);
                }
                catch(err) {
                    return cb(new Error('fsExt.requireAsync: eval data failed').details({ filePath:filePath, cause:err }));
                }
            }
            
            _modules[filePath] = loadingModule.exports || loadingModule.module.exports;
            return cb(null, _modules[filePath]);
        });
        
    }
    else cb(null, _modules[filePath]);
}