异步函数封装请确保异步性(Javascript需要养成的良好习惯)

  背景假设:

  你有许多的配置信息存放在服务器上,因为配置太多,不希望每次都把所有的配置信息都写到前端,希望能需要用的时候再获取就好了。

因为Javascript单线程运行,你不希望堵塞ui渲染于是你专门写了个异步获取函数(ajax获取后台信息)

var getConfig=function(key,callback){    $.get('/config/'+key,function(config){        callback(null,config);    },'json');};//使用该函数getConfig(1,function(err,config){    if(err){        return console.log(err);    }    console.log(config);});

于是就可以欢快的使用它了。

  你发现你的Javascript里面调用它的地方很多,每次都发起一个请求太费时间和资源,于是你打算给它加上缓存(为了说明问题,我们不用高阶函数:)  )。

 

var getConfig = (function () {
    var _configs = {};//缓存容器
    return function (key, callback) {
        if (_configs[key] === undefined) {
            $.get('/config/' + key, function (config) {
                _configs[key] = config;
                callback(null, config);
            }, 'json');
        } else {
            return callback(null,_configs[key]);
        }
    };
})();

 

  于是你就可以欢快的使用配置了,获取过的配置都会被缓存。

  问题就出在这里,你运行以下代码

getConfig('db',function(err,config){
    console.log(config);//输出2
});
console.log('hello world');//输出1

  在我们添加缓存之前的版本,我们每次运行该代码  都是先输出1,再输出2。但是在我们加了缓存后,假如缓存存在的话,我们的输出就变成了先输出2,再输出1,这样的情况或许在我们的事例中好像没有什么影响,但在有些情景下将会留下一些比较奇怪的bug。

  这里的问题就是一个异步的不一致性问题,解决该问题可以这样。

var getConfig = (function () {
    var _configs = {};//缓存容器
    return function (key, callback) {
        if (_configs[key] === undefined) {
            $.get('/config/' + key, function (config) {
                _configs[key] = config;
                return callback(null, config);
            }, 'json');
        } else {
            setTimeout(function(){//改动的地方
                return callback(null,_configs[key]);
            },0);
        }
    };
})();

  我们需要把callback的运行放到下一个tick中才运行,已保持getConfig函数的异步性质。

 

结论:当封装函数的时候如果是一个异步函数的时候,需要确保函数的回调一直都是异步的。(这里是个新手(如我)常见的小问题,但是养成一种好的习惯,有助于保证代码的健壮性)

     

 PS:如果你需要确保一个函数是一个异步函数,可以考虑下 的  async.ensureAsync方法(巧妙的运用同异步的差异实现)

  如果你考虑对你的方法做结果缓存,同样可以考虑  的 async.memoize方法(更完整,不单单缓存结果,还为高并发情况做了处理多个相同请求并发只会触发一次计算)。

ensureAsync
26人参与, 0条评论 登录后显示评论回复

你需要登录后才能评论 登录/ 注册