mem模塊 緩存模塊

mem模塊

mem的github地址

作用

緩存函數的運行結果,當參數一樣的時候,不再運行,直接讀取緩存值

使用

const mem = require('mem');
const m = mem(fn[,options]);
fn:你想運行的函數
options:mem的設置,包括設置緩存時間、緩存key值算法、緩存存儲、統計等

具體請參見文檔

同步使用
const mem = require('mem');

let i = 0;

function sum() {
  ++i;
}

const m = mem(sum);

m('h');//i=1
m('h');//i=1
m('m');//i=2
m('m');//i=2
異步使用
const mem = require('mem');

let i = 0;

async function sum() {
  ++i;
}

const m = mem(sum);

(async function () {
  await m();//i=1
  await m();//i=1
  await m('i');//i=2
})();

源碼學習

源碼暴露出兩個函數:

  • mem:主函數
  • clear :清除緩存函數

主函數

主函實現:

  • 計算key值,將數據存儲於Map中
  • 數據去重,不存儲已經存在過的key值
  • 超過設置maxAge時間清除緩存
  • 將函數作爲key值,存入WeekMap中
1、計算key值
// 默認使用defaultCacheKey函數
const defaultCacheKey = (...args) => {
	if (args.length === 0) {
		return '__defaultKey';
	}

	if (args.length === 1) {
		const [firstArgument] = args;
		if (
			firstArgument === null ||
			firstArgument === undefined ||
			(typeof firstArgument !== 'function' && typeof firstArgument !== 'object')
		) {
			return firstArgument;
		}
	}

	return JSON.stringify(args);
};

如上是作者計算緩存key的函數,將計算後的key做爲cache的鍵。當然也可以自己傳入key的計算函數:

const mem = require('mem');
const m = mem(fn,{
	cacheKey:fn //此處可以傳入計算key的函數
});

計算key的時候,判斷有些不嚴謹

  • 傳入值 於 期望返回的不符:當傳入NaN、Object類型時,可能會造成與期望不符,例如:
const mem = require('mem');

let i = 0;

function sum() {
  ++i;
}

const m = mem(sum);

m(NaN);// i=1
m(NaN);// i=1
m({ a: 1 });// i=2
m({ a: 1 });// i=3
// 但是 NaN是不等於NaN,{a:1}也不等於{a:1},而他們的得到值是一樣的
  • 某些值使用JSON.stringfy()會出現問題:如正則表達式和函數
m(function b() {});// i=1
m(function a() {});// i=1
m(/a/);// i=2
m(/b/);// i=2
2、數據去重
const memoized = function (...args) {
		const key = options.cacheKey(...args);

		if (cache.has(key)) {
			const c = cache.get(key);

			return c.data;
		}

		const ret = fn.call(this, ...args);

		setData(key, ret);

		if (isPromise(ret) && options.cachePromiseRejection === false) {
			// Remove rejected promises from cache unless `cachePromiseRejection` is set to `true`
			ret.catch(() => cache.delete(key));
		}

		return ret;
	};
	// 作者使用Map的has方法判斷去重
	// 	cache(Map類型)在這裏充當存儲空間,是優於Object的,因爲Map可以存儲任意類型的key值,而Object只能存儲字符串和數字的key值
3、maxAge實現
// mem/index.js文件中
const mapAgeCleaner = require('map-age-cleaner');
...
  if (typeof options.maxAge === 'number') {
    mapAgeCleaner(options.cache);
  }
...
// 使用mapAgeCleaner函數,覆蓋了Map.set()方法,調用了cleanup()方法。

在cleanup內部有如下判斷超時的機制
...
// map-age-cleaner/index.js文件中
   const delay = item[ 1 ][ property ] - Date.now();
      if (delay <= 0) {  // 此處判斷是否超時
        // Remove the item immediately if the delay is equal to or below 0
        map.delete(item[ 0 ]);// 超時了則刪除當前key的數據
        processingDeferred.resolve();
        return;
      }
...
4、緩存函數
...
	mimicFn(memoized, fn);

	cacheStore.set(memoized, options.cache);
...
// 以函數做爲鍵名,cache作爲值,存入cacheStore(weekMap類型)中
// cacheStore是weekMap類型,在此處使用該類型的作用時,weekMap內部的key值是弱引用,當外部引用消失的時候,自動銷燬內部對應的鍵值,防止內存溢出。

清除函數

調用clear方法,手動清楚緩存

module.exports.clear = fn => {
	const cache = cacheStore.get(fn);

	if (cache && typeof cache.clear === 'function') {
		cache.clear();
	}
};

結論

優點:

  • 快速緩存函數運行結果

不足:

  • 在key計算時,不夠嚴謹
  • 未設置默認的maxAge時間(當未設置maxAge則爲永久緩存),可能會造成內存泄漏
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章