轻量级node-cache源码分析一波

最近有接触到缓存这一块,无意间发现了这个清凉级的缓存,而且也不复杂,刚好作为源码练习一下,戳此观赏源码 -> 点我,点我

话不多说,直接来看看...

先从如何使用来入手,毕竟这是最直观的


  • put = function(key, value, time, timeoutCallback)
    • 简洁的存储一个value
    • 如果没有带入time,这个value会永久存储
    • 在给定的time之后会移除value,time是毫秒(via setTimeout)
    • timeoutCallback是可选的参数,它会在传入的kye,value过期之后激活
    • 返回缓存的value
  • get = function(key)
    • 在缓存中查找对应key的value
    • 如果缓存中没有value,返回null
  • del = function(key)
    • 删除一个key,返回boolean来判断key是否被删除
  • clear = function()
    • 删除所有的key
  • size = function()
    • 返回当前缓存中的缓存数量
  • memsize = function()
    • 返回当前缓存所占的空间
    • 通常== size(),除非setTimeout出了问题
  • debug = function(bool)
    • 开启/关闭debugging模式
  • hits = function()
    • 返回缓存命中数(只在debug模式下监听)
  • misses = function()
    • 返回缓存未命中数(只在debug模式下监听)
  • keys = funciton()
    • 返回所有缓存中的keys
  • exportJson = function()
    • 将所有缓存数据转成JSON返回
    • 所有timeoutCallbacks将被忽略
  • importJson = function(json:string, options:{skipDuplicates:boolean})
    • 合并所有在之前的调用中export进入缓存的数据
    • import之前的所有已存在的数据都将保存在缓存中
    • skipDuplicatestrue时所有重复的key都将被重写
    • 所有数据被暴露后,在导入时都将过期(但是他们的回调不会执行)
    • 可用options
      • skipDuplicates:如果为true,所有重复的keys在导入的时候都会被跳过。默认为false
    • 返回新的缓存数量
  • Cache = function()
    • Cache构造器
    • 需要注意require('cache')会返回Cache的默认实例
    • require('cache').Cache才是实际的类。

看了这些api,我们大致知道了用法,而且文档也特别详细。相信对阅读源码很是方便,下面直接上。

作者好久没更新了,不过这并不影响它有900多颗星星:smile:, 文件也很直观,需要源码就在index.js里面。

index.js

'use strict';

function Cache () {
  var _cache = Object.create(null);
  var _hitCount = 0;
  var _missCount = 0;
  var _size = 0;
  var _debug = false;

  this.put = function(key, value, time, timeoutCallback) {
    if (_debug) {
      console.log('caching: %s = %j (@%s)', key, value, time);
    }

    if (typeof time !== 'undefined' && (typeof time !== 'number' || isNaN(time) || time <= 0)) {
      throw new Error('Cache timeout must be a positive number');
    } else if (typeof timeoutCallback !== 'undefined' && typeof timeoutCallback !== 'function') {
      throw new Error('Cache timeout callback must be a function');
    }

    var oldRecord = _cache[key];
    if (oldRecord) {
      clearTimeout(oldRecord.timeout);
    } else {
      _size++;
    }

    var record = {
      value: value,
      expire: time + Date.now()
    };

    if (!isNaN(record.expire)) {
      record.timeout = setTimeout(function() {
        _del(key);
        if (timeoutCallback) {
          timeoutCallback(key, value);
        }
      }.bind(this), time);
    }

    _cache[key] = record;

    return value;
  };

  this.del = function(key) {
    var canDelete = true;

    var oldRecord = _cache[key];
    if (oldRecord) {
      clearTimeout(oldRecord.timeout);
      if (!isNaN(oldRecord.expire) && oldRecord.expire < Date.now()) {
        canDelete = false;
      }
    } else {
      canDelete = false;
    }

    if (canDelete) {
      _del(key);
    }

    return canDelete;
  };

  function _del(key){
    _size--;
    delete _cache[key];
  }

  this.clear = function() {
    for (var key in _cache) {
      clearTimeout(_cache[key].timeout);
    }
    _size = 0;
    _cache = Object.create(null);
    if (_debug) {
      _hitCount = 0;
      _missCount = 0;
    }
  };

  this.get = function(key) {
    var data = _cache[key];
    if (typeof data != "undefined") {
      if (isNaN(data.expire) || data.expire >= Date.now()) {
        if (_debug) _hitCount++;
        return data.value;
      } else {
        // free some space
        if (_debug) _missCount++;
        _size--;
        delete _cache[key];
      }
    } else if (_debug) {
      _missCount++;
    }
    return null;
  };

  this.size = function() {
    return _size;
  };

  this.memsize = function() {
    var size = 0,
      key;
    for (key in _cache) {
      size++;
    }
    return size;
  };

  this.debug = function(bool) {
    _debug = bool;
  };

  this.hits = function() {
    return _hitCount;
  };

  this.misses = function() {
    return _missCount;
  };

  this.keys = function() {
    return Object.keys(_cache);
  };

  this.exportJson = function() {
    var plainJsCache = {};

    // Discard the `timeout` property.
    // Note: JSON doesnt support NaN, so convert it to NaN.
    for (var key in _cache) {
      var record = _cache[key];
      plainJsCache[key] = {
        value: record.value,
        expire: record.expire || 'NaN',
      };
    }

    return JSON.stringify(plainJsCache);
  };

  this.importJson = function(jsonToImport, options) {
    var cacheToImport = JSON.parse(jsonToImport);
    var currTime = Date.now();

    var skipDuplicates = options && options.skipDuplicates;

    for (var key in cacheToImport) {
      if (cacheToImport.hasOwnProperty(key)) {
        if (skipDuplicates) {
          var existingRecord = _cache[key];
          if (existingRecord) {
            if (_debug) {
              console.log('Skipping duplicate imported key \'%s\'', key);
            }
            continue;
          }
        }

        var record = cacheToImport[key];

        // record.expire could be `'NaN'` if no expiry was set.
        // Try to subtract from it; a string minus a number is `NaN`, which is perfectly fine here.
        var remainingTime = record.expire - currTime;

        if (remainingTime <= 0) {
          // Delete any record that might exist with the same key, since this key is expired.
          this.del(key);
          continue;
        }

        // Remaining time must now be either positive or `NaN`,
        // but `put` will throw an error if we try to give it `NaN`.
        remainingTime = remainingTime > 0 ? remainingTime : undefined;

        this.put(key, record.value, remainingTime);
      }
    }

    return this.size();
  };
}

module.exports = new Cache();
module.exports.Cache = Cache;

复制代码

这样一看,当然没有头绪呀,这么长一段代码。。。 下面拆分一下:

Part 1:


我们弱化了具体实现,总览一下大概。发现其实Cache只是一个函数而已,通过看最后两行

module.exports = new Cache();
module.exports.Cache = Cache;
复制代码

发现这正好是api:Cache = function()的解释。我们还知道啥?其实这里就可以看出很多东西,Cache里面有_cache, _hitCount, _missCount, _size, _debug这几个变量,不难猜出一些东西:

变量说明
_cache是初始化的一个对象,下面可以使用到
_hitCount用于缓存命中的计数
_missCount用户缓存未命中的计数
_size缓存中的数量
_debug开启debug模式

嗯,除此之外,就是一些属性方法,具体看看实现吧!

Part 2:


下面就看看第一个put方法是做了啥?

  • put = function(key, value, time, timeoutCallback)
    • 简洁的存储一个value
    • 如果没有带入time,这个value会永久存储
    • 在给定的time之后会移除value,time是毫秒(via setTimeout)
    • timeoutCallback是可选的参数,它会在传入的kye,value过期之后激活
    • 返回缓存的value

Part 3:


在看看get方法

  • get = function(key)
    • 在缓存中查找对应key的value
    • 如果缓存中没有value,返回null

Part 4:


剩下的差不多也是和上面类似,就不一一分析啦,直接看:

  • exportJson = function()
    • 将所有缓存数据转成JSON返回
    • 所有timeoutCallbacks将被忽略

  • importJson = function(json:string, options:{skipDuplicates:boolean})
    • 合并所有在之前的调用中export进入缓存的数据
    • import之前的所有已存在的数据都将保存在缓存中
    • skipDuplicatestrue时所有重复的key都将被重写
    • 所有数据被暴露后,在导入时都将过期(但是他们的回调不会执行)
    • 可用options
      • skipDuplicates:如果为true,所有重复的keys在导入的时候都会被跳过。默认为false
    • 返回新的缓存数量

Part 5:

虽然这个node-cache没那么难,写的简洁易懂,也是一种能力呀。就当学习源码的一道开胃菜。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值