├── .gitignore ├── .npmignore ├── .travis.yml ├── LICENSE ├── README.md ├── cjs ├── index.js └── package.json ├── esm └── index.js ├── index.js ├── new.js ├── package.json ├── rollup ├── index.config.js └── new.config.js └── test ├── benchmark-basic-lru.js ├── benchmark-lru-cache.js ├── benchmark-lru-map.js ├── benchmark-lru.js ├── benchmark-quick-lru.js └── index.js /.gitignore: -------------------------------------------------------------------------------- 1 | .nyc_output/ 2 | node_modules/ 3 | package-lock.json 4 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | .nyc_output/ 2 | node_modules/ 3 | rollup/ 4 | test/ 5 | package-lock.json 6 | .travis.yml 7 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - stable 4 | git: 5 | depth: 1 6 | branches: 7 | only: 8 | - master 9 | - /^greenkeeper/.*$/ 10 | after_success: 11 | - "npm run coveralls" 12 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | ISC License 2 | 3 | Copyright (c) 2020, Andrea Giammarchi, @WebReflection 4 | 5 | Permission to use, copy, modify, and/or distribute this software for any 6 | purpose with or without fee is hereby granted, provided that the above 7 | copyright notice and this permission notice appear in all copies. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH 10 | REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 11 | AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, 12 | INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 13 | LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 14 | OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 15 | PERFORMANCE OF THIS SOFTWARE. 16 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # basic-lru 2 | 3 | **Social Media Photo by [Mr Cup / Fabien Barral](https://unsplash.com/@iammrcup) on [Unsplash](https://unsplash.com/)** 4 | 5 | [![Build Status](https://travis-ci.com/WebReflection/basic-lru.svg?branch=master)](https://travis-ci.com/WebReflection/basic-lru) [![Coverage Status](https://coveralls.io/repos/github/WebReflection/basic-lru/badge.svg?branch=master)](https://coveralls.io/github/WebReflection/basic-lru?branch=master) 6 | 7 | A fast and lightweight, as in [684 bytes](https://unpkg.com/basic-lru), Map based LRU implementation. 8 | 9 | ```js 10 | import LRU from 'basic-lru'; 11 | const LRU = require('basic-lru'); 12 | // https://unpkg.com/basic-lru to have LRU globally 13 | 14 | // new LRU(maxSize) 15 | const lru = new LRU(100); 16 | 17 | // new LRU({max}) or new LRU({maxSize}) 18 | const lru = new LRU({max: 1000}); 19 | 20 | // new LRU({maxAge}) in milliseconds 21 | const lru = new LRU({maxAge: 1000}); 22 | 23 | // variants 24 | const lru = new LRU({max: 100, maxAge: 1000}); 25 | const lru = new LRU({maxSize: 100, maxAge: 1000}); 26 | ``` 27 | 28 | ### About 29 | 30 | This module is a drop-in replacement for any [Map](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map) instance, and it's mostly 100% compatible with [lru](https://github.com/chriso/lru), [lru-cache](https://github.com/isaacs/node-lru-cache), [quick-lru](https://github.com/sindresorhus/quick-lru), and [lru-map](https://github.com/bchociej/lru-map) modules. 31 | 32 | Differently from other modules, this one has the least amount of LOC, zero dependencies, and it's based on _ES2015_ class capability to extend the native `Map`. 33 | 34 | 35 | ### The Map Extend Differences 36 | 37 | The only difference from a real Map, beside implementing a classic LRU cache, is the `peek(key)` method, to access an entry without flagging its access time anyhow, and borrowed from other libraries, plus a constructor, also borrowed from other libraries, that accepts either an integer, or an options object with `max`, or `maxSize`, and a `maxAge` property, where all of these are optional too. 38 | -------------------------------------------------------------------------------- /cjs/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | const {now} = Date; 3 | const {defineProperties} = Object; 4 | const {iterator} = Symbol; 5 | const zero = {writable: true, value: 0}; 6 | 7 | module.exports = class LRU extends Map { 8 | 9 | // constructor overload, same lru signature 10 | constructor(options) { 11 | const n = typeof options === 'number'; 12 | const _max = (n ? options : (options.max || options.maxSize)) || Infinity; 13 | const _age = n ? 0 : (options.maxAge || 0); 14 | defineProperties(super(), { 15 | _dropExpired: {value: this._dropExpired.bind(this)}, 16 | _dropCount: {value: this._dropCount.bind(this)}, 17 | _max: {value: _max}, 18 | _age: {value: _age}, 19 | _timer: zero, 20 | _drop: zero, 21 | _count: zero 22 | }); 23 | } 24 | 25 | // same Map signature overloads 26 | get(key) { 27 | const entry = super.get(key); 28 | if (entry) { 29 | entry.time = now(); 30 | return entry.value; 31 | } 32 | } 33 | set(key, value) { 34 | const {_max, _age} = this; 35 | if (!this._drop && this.size === _max) 36 | this._drop = setTimeout(this._dropCount); 37 | if (_age) { 38 | clearTimeout(this._timer); 39 | this._timer = setTimeout(this._dropExpired, _age); 40 | } 41 | return super.set(key, { 42 | count: this._count++, 43 | time: now(), 44 | value 45 | }); 46 | } 47 | forEach(callback, self) { 48 | return super.forEach( 49 | ({value}, key) => callback.call(self, value, key, this) 50 | ); 51 | } 52 | 53 | // extra methods 54 | peek(key) { 55 | const entry = super.get(key); 56 | if (entry) 57 | return entry.value; 58 | } 59 | 60 | // iterators based overloads 61 | * entries() { 62 | yield * this[iterator](); 63 | } 64 | * values() { 65 | for (const [_, {value}] of super[iterator]()) 66 | yield value; 67 | } 68 | * [iterator]() { 69 | for (const [key, {value}] of super[iterator]()) 70 | yield [key, value]; 71 | } 72 | 73 | // private methods (to be moved as #methods) 74 | _dropCount() { 75 | this._drop = 0; 76 | [...super[iterator]()] 77 | .sort(([_1, entry1], [_2, entry2]) => { 78 | const prop = entry1.time === entry2.time ? 'count' : 'time'; 79 | return entry2[prop] - entry1[prop]; 80 | }) 81 | .slice(this._max) 82 | .forEach(([key]) => this.delete(key)); 83 | } 84 | _dropExpired() { 85 | const expiration = now() - this._age; 86 | super.forEach(({time}, key, self) => { 87 | if (time < expiration) 88 | self.delete(key); 89 | }); 90 | } 91 | }; 92 | -------------------------------------------------------------------------------- /cjs/package.json: -------------------------------------------------------------------------------- 1 | {"type":"commonjs"} -------------------------------------------------------------------------------- /esm/index.js: -------------------------------------------------------------------------------- 1 | const {now} = Date; 2 | const {defineProperties} = Object; 3 | const {iterator} = Symbol; 4 | const zero = {writable: true, value: 0}; 5 | 6 | export default class LRU extends Map { 7 | 8 | // constructor overload, same lru signature 9 | constructor(options) { 10 | const n = typeof options === 'number'; 11 | const _max = (n ? options : (options.max || options.maxSize)) || Infinity; 12 | const _age = n ? 0 : (options.maxAge || 0); 13 | defineProperties(super(), { 14 | _dropExpired: {value: this._dropExpired.bind(this)}, 15 | _dropCount: {value: this._dropCount.bind(this)}, 16 | _max: {value: _max}, 17 | _age: {value: _age}, 18 | _timer: zero, 19 | _drop: zero, 20 | _count: zero 21 | }); 22 | } 23 | 24 | // same Map signature overloads 25 | get(key) { 26 | const entry = super.get(key); 27 | if (entry) { 28 | entry.time = now(); 29 | return entry.value; 30 | } 31 | } 32 | set(key, value) { 33 | const {_max, _age} = this; 34 | if (!this._drop && this.size === _max) 35 | this._drop = setTimeout(this._dropCount); 36 | if (_age) { 37 | clearTimeout(this._timer); 38 | this._timer = setTimeout(this._dropExpired, _age); 39 | } 40 | return super.set(key, { 41 | count: this._count++, 42 | time: now(), 43 | value 44 | }); 45 | } 46 | forEach(callback, self) { 47 | return super.forEach( 48 | ({value}, key) => callback.call(self, value, key, this) 49 | ); 50 | } 51 | 52 | // extra methods 53 | peek(key) { 54 | const entry = super.get(key); 55 | if (entry) 56 | return entry.value; 57 | } 58 | 59 | // iterators based overloads 60 | * entries() { 61 | yield * this[iterator](); 62 | } 63 | * values() { 64 | for (const [_, {value}] of super[iterator]()) 65 | yield value; 66 | } 67 | * [iterator]() { 68 | for (const [key, {value}] of super[iterator]()) 69 | yield [key, value]; 70 | } 71 | 72 | // private methods (to be moved as #methods) 73 | _dropCount() { 74 | this._drop = 0; 75 | [...super[iterator]()] 76 | .sort(([_1, entry1], [_2, entry2]) => { 77 | const prop = entry1.time === entry2.time ? 'count' : 'time'; 78 | return entry2[prop] - entry1[prop]; 79 | }) 80 | .slice(this._max) 81 | .forEach(([key]) => this.delete(key)); 82 | } 83 | _dropExpired() { 84 | const expiration = now() - this._age; 85 | super.forEach(({time}, key, self) => { 86 | if (time < expiration) 87 | self.delete(key); 88 | }); 89 | } 90 | }; 91 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | var LRU = (function (exports) { 2 | 'use strict'; 3 | 4 | const {now} = Date; 5 | const {defineProperties} = Object; 6 | const {iterator} = Symbol; 7 | const zero = {writable: true, value: 0}; 8 | 9 | class LRU extends Map { 10 | 11 | // constructor overload, same lru signature 12 | constructor(options) { 13 | const n = typeof options === 'number'; 14 | const _max = (n ? options : (options.max || options.maxSize)) || Infinity; 15 | const _age = n ? 0 : (options.maxAge || 0); 16 | defineProperties(super(), { 17 | _dropExpired: {value: this._dropExpired.bind(this)}, 18 | _dropCount: {value: this._dropCount.bind(this)}, 19 | _max: {value: _max}, 20 | _age: {value: _age}, 21 | _timer: zero, 22 | _drop: zero, 23 | _count: zero 24 | }); 25 | } 26 | 27 | // same Map signature overloads 28 | get(key) { 29 | const entry = super.get(key); 30 | if (entry) { 31 | entry.time = now(); 32 | return entry.value; 33 | } 34 | } 35 | set(key, value) { 36 | const {_max, _age} = this; 37 | if (!this._drop && this.size === _max) 38 | this._drop = setTimeout(this._dropCount); 39 | if (_age) { 40 | clearTimeout(this._timer); 41 | this._timer = setTimeout(this._dropExpired, _age); 42 | } 43 | return super.set(key, { 44 | count: this._count++, 45 | time: now(), 46 | value 47 | }); 48 | } 49 | forEach(callback, self) { 50 | return super.forEach( 51 | ({value}, key) => callback.call(self, value, key, this) 52 | ); 53 | } 54 | 55 | // extra methods 56 | peek(key) { 57 | const entry = super.get(key); 58 | if (entry) 59 | return entry.value; 60 | } 61 | 62 | // iterators based overloads 63 | * entries() { 64 | yield * this[iterator](); 65 | } 66 | * values() { 67 | for (const [_, {value}] of super[iterator]()) 68 | yield value; 69 | } 70 | * [iterator]() { 71 | for (const [key, {value}] of super[iterator]()) 72 | yield [key, value]; 73 | } 74 | 75 | // private methods (to be moved as #methods) 76 | _dropCount() { 77 | this._drop = 0; 78 | [...super[iterator]()] 79 | .sort(([_1, entry1], [_2, entry2]) => { 80 | const prop = entry1.time === entry2.time ? 'count' : 'time'; 81 | return entry2[prop] - entry1[prop]; 82 | }) 83 | .slice(this._max) 84 | .forEach(([key]) => this.delete(key)); 85 | } 86 | _dropExpired() { 87 | const expiration = now() - this._age; 88 | super.forEach(({time}, key, self) => { 89 | if (time < expiration) 90 | self.delete(key); 91 | }); 92 | } 93 | } 94 | 95 | exports.default = LRU; 96 | 97 | return exports; 98 | 99 | }({}).default); 100 | -------------------------------------------------------------------------------- /new.js: -------------------------------------------------------------------------------- 1 | var LRU=function(e){"use strict";const{now:t}=Date,{defineProperties:r}=Object,{iterator:s}=Symbol,i={writable:!0,value:0};class o extends Map{constructor(e){const t="number"==typeof e,s=(t?e:e.max||e.maxSize)||1/0,o=t?0:e.maxAge||0;r(super(),{_dropExpired:{value:this._dropExpired.bind(this)},_dropCount:{value:this._dropCount.bind(this)},_max:{value:s},_age:{value:o},_timer:i,_drop:i,_count:i})}get(e){const r=super.get(e);if(r)return r.time=t(),r.value}set(e,r){const{_max:s,_age:i}=this;return this._drop||this.size!==s||(this._drop=setTimeout(this._dropCount)),i&&(clearTimeout(this._timer),this._timer=setTimeout(this._dropExpired,i)),super.set(e,{count:this._count++,time:t(),value:r})}forEach(e,t){return super.forEach(({value:r},s)=>e.call(t,r,s,this))}peek(e){const t=super.get(e);if(t)return t.value}*entries(){yield*this[s]()}*values(){for(const[e,{value:t}]of super[s]())yield t}*[s](){for(const[e,{value:t}]of super[s]())yield[e,t]}_dropCount(){this._drop=0,[...super[s]()].sort(([e,t],[r,s])=>{const i=t.time===s.time?"count":"time";return s[i]-t[i]}).slice(this._max).forEach(([e])=>this.delete(e))}_dropExpired(){const e=t()-this._age;super.forEach(({time:t},r,s)=>{t { 58 | console.log(String(event.target)) 59 | if (event.target.error) 60 | console.error(event.target.error) 61 | }) 62 | .run(); 63 | -------------------------------------------------------------------------------- /test/benchmark-lru-cache.js: -------------------------------------------------------------------------------- 1 | // https://github.com/isaacs/node-lru-cache/blob/master/benchmark/index.js 2 | 3 | 'use strict' 4 | 5 | console.log('\x1b[7m Benchmarking \x1b[1mlru-cache \x1b[0m'); 6 | 7 | var benchmark = require('benchmark') 8 | 9 | // function LRU() { const lru = new Map; lru.peek = lru.get; return lru; } 10 | var LRU = require('lru-cache') 11 | 12 | var suite = new benchmark.Suite() 13 | 14 | function add(name, fn) { 15 | suite.add(name, fn) 16 | } 17 | 18 | // SET 19 | var lru1 = new LRU({ 20 | max: 1000 21 | }) 22 | var lru1Counter = 0 23 | 24 | add(' set', function() { 25 | lru1.set('key' + (lru1Counter++), 'value') 26 | }) 27 | 28 | // GET and PEEK 29 | var lru2 = new LRU({ 30 | max: 1000 31 | }) 32 | var lru2Counter = 0 33 | 34 | for (var i = 0; i < 1000; i++) 35 | lru2.set('key' + i, 'value') 36 | 37 | add(' get', function() { 38 | lru2.get('key' + (lru2Counter++) % 1000) 39 | }) 40 | 41 | add(' peek', function() { 42 | lru2.peek('key' + (lru2Counter++) % 1000) 43 | }) 44 | 45 | // SET with maxAge 46 | var lru3 = new LRU({ 47 | max: 1000, 48 | maxAge: 1 49 | }) 50 | var lru3Counter = 0 51 | 52 | add(' set with `maxAge`', function() { 53 | lru3.set('key' + (lru3Counter++), 'value', 100000) 54 | }) 55 | 56 | suite 57 | .on('cycle', (event) => { 58 | console.log(String(event.target)) 59 | if (event.target.error) 60 | console.error(event.target.error) 61 | }) 62 | .run(); 63 | -------------------------------------------------------------------------------- /test/benchmark-lru-map.js: -------------------------------------------------------------------------------- 1 | // https://github.com/isaacs/node-lru-cache/blob/master/benchmark/index.js 2 | 3 | 'use strict' 4 | 5 | console.log('\x1b[7m Benchmarking \x1b[1mlru-map \x1b[0m'); 6 | 7 | var benchmark = require('benchmark') 8 | 9 | // function LRU() { const lru = new Map; lru.peek = lru.get; return lru; } 10 | var LRU = require('lru-map') 11 | 12 | var suite = new benchmark.Suite() 13 | 14 | function add(name, fn) { 15 | suite.add(name, fn) 16 | } 17 | 18 | // SET 19 | var lru1 = new LRU({ 20 | maxSize: 1000 21 | }) 22 | var lru1Counter = 0 23 | 24 | add(' set', function() { 25 | lru1.set('key' + (lru1Counter++), 'value') 26 | }) 27 | 28 | // GET and PEEK 29 | var lru2 = new LRU({ 30 | maxSize: 1000 31 | }) 32 | var lru2Counter = 0 33 | 34 | for (var i = 0; i < 1000; i++) 35 | lru2.set('key' + i, 'value') 36 | 37 | add(' get', function() { 38 | lru2.get('key' + (lru2Counter++) % 1000) 39 | }) 40 | 41 | add(' peek', function() { 42 | lru2.peek('key' + (lru2Counter++) % 1000) 43 | }) 44 | 45 | // SET with maxAge 46 | var lru3 = new LRU({ 47 | maxSize: 1000, 48 | maxAge: 1 49 | }) 50 | var lru3Counter = 0 51 | 52 | add(' set with `maxAge`', function() { 53 | lru3.set('key' + (lru3Counter++), 'value', 100000) 54 | }) 55 | 56 | suite 57 | .on('cycle', (event) => { 58 | console.log(String(event.target)) 59 | if (event.target.error) 60 | console.error(event.target.error) 61 | }) 62 | .run(); 63 | -------------------------------------------------------------------------------- /test/benchmark-lru.js: -------------------------------------------------------------------------------- 1 | // https://github.com/isaacs/node-lru-cache/blob/master/benchmark/index.js 2 | 3 | 'use strict' 4 | 5 | console.log('\x1b[7m Benchmarking \x1b[1mlru \x1b[0m'); 6 | 7 | var benchmark = require('benchmark') 8 | 9 | // function LRU() { const lru = new Map; lru.peek = lru.get; return lru; } 10 | var LRU = require('lru') 11 | 12 | var suite = new benchmark.Suite() 13 | 14 | function add(name, fn) { 15 | suite.add(name, fn) 16 | } 17 | 18 | // SET 19 | var lru1 = new LRU({ 20 | max: 1000 21 | }) 22 | var lru1Counter = 0 23 | 24 | add(' set', function() { 25 | lru1.set('key' + (lru1Counter++), 'value') 26 | }) 27 | 28 | // GET and PEEK 29 | var lru2 = new LRU({ 30 | max: 1000 31 | }) 32 | var lru2Counter = 0 33 | 34 | for (var i = 0; i < 1000; i++) 35 | lru2.set('key' + i, 'value') 36 | 37 | add(' get', function() { 38 | lru2.get('key' + (lru2Counter++) % 1000) 39 | }) 40 | 41 | add(' peek', function() { 42 | lru2.peek('key' + (lru2Counter++) % 1000) 43 | }) 44 | 45 | // SET with maxAge 46 | var lru3 = new LRU({ 47 | max: 1000, 48 | maxAge: 1 49 | }) 50 | var lru3Counter = 0 51 | 52 | add(' set with `maxAge`', function() { 53 | lru3.set('key' + (lru3Counter++), 'value', 100000) 54 | }) 55 | 56 | suite 57 | .on('cycle', (event) => { 58 | console.log(String(event.target)) 59 | if (event.target.error) 60 | console.error(event.target.error) 61 | }) 62 | .run(); 63 | -------------------------------------------------------------------------------- /test/benchmark-quick-lru.js: -------------------------------------------------------------------------------- 1 | // https://github.com/isaacs/node-lru-cache/blob/master/benchmark/index.js 2 | 3 | 'use strict' 4 | 5 | console.log('\x1b[7m Benchmarking \x1b[1mquick-lru \x1b[0m'); 6 | 7 | var benchmark = require('benchmark') 8 | 9 | // function LRU() { const lru = new Map; lru.peek = lru.get; return lru; } 10 | var LRU = require('quick-lru') 11 | 12 | var suite = new benchmark.Suite() 13 | 14 | function add(name, fn) { 15 | suite.add(name, fn) 16 | } 17 | 18 | // SET 19 | var lru1 = new LRU({ 20 | maxSize: 1000 21 | }) 22 | var lru1Counter = 0 23 | 24 | add(' set', function() { 25 | lru1.set('key' + (lru1Counter++), 'value') 26 | }) 27 | 28 | // GET and PEEK 29 | var lru2 = new LRU({ 30 | maxSize: 1000 31 | }) 32 | var lru2Counter = 0 33 | 34 | for (var i = 0; i < 1000; i++) 35 | lru2.set('key' + i, 'value') 36 | 37 | add(' get', function() { 38 | lru2.get('key' + (lru2Counter++) % 1000) 39 | }) 40 | 41 | add(' peek', function() { 42 | lru2.peek('key' + (lru2Counter++) % 1000) 43 | }) 44 | 45 | // SET with maxAge 46 | var lru3 = new LRU({ 47 | maxSize: 1000, 48 | maxAge: 1 49 | }) 50 | var lru3Counter = 0 51 | 52 | add(' set with `maxAge`', function() { 53 | lru3.set('key' + (lru3Counter++), 'value', 100000) 54 | }) 55 | 56 | suite 57 | .on('cycle', (event) => { 58 | console.log(String(event.target)) 59 | if (event.target.error) 60 | console.error(event.target.error) 61 | }) 62 | .run(); 63 | -------------------------------------------------------------------------------- /test/index.js: -------------------------------------------------------------------------------- 1 | const LRU = require('../cjs'); 2 | 3 | const sleep = ms => new Promise($ => setTimeout($, ms)); 4 | 5 | const item1 = ['a', 1]; 6 | const item2 = ['b', 2]; 7 | const item3 = ['c', 3]; 8 | 9 | const lru1 = new LRU(2); 10 | const lru2 = new LRU({max: 2}); 11 | const lru3 = new LRU({maxSize: 2, maxAge: 200}); 12 | 13 | console.assert(lru1.set(...item1) === lru1, 1); 14 | console.assert(lru1.get(item1[0]) === item1[1], 2); 15 | console.assert(lru1.set(...item2) === lru1, 3); 16 | console.assert(lru1.set(...item3) === lru1, 4); 17 | 18 | (async () => { 19 | await sleep(10); 20 | console.assert(lru1.get(item1[0]) === void 0, 5); 21 | console.assert(JSON.stringify([...lru1]) === JSON.stringify([item2, item3]), 6); 22 | 23 | lru1.forEach(Object); 24 | lru1.delete(item2[0]); 25 | lru1.forEach(function (value, key, map) { 26 | console.assert(this === lru2, 7); 27 | console.assert(map === lru1, 8); 28 | console.assert(key === item3[0], 9); 29 | console.assert(value === item3[1], 10); 30 | }, lru2); 31 | 32 | for (const [key, value] of lru1) { 33 | console.assert(key === item3[0], 11); 34 | console.assert(value === item3[1], 12); 35 | } 36 | 37 | for (const [key, value] of lru1.entries()) { 38 | console.assert(key === item3[0], 11); 39 | console.assert(value === item3[1], 12); 40 | } 41 | 42 | for (const value of lru1.values()) { 43 | console.assert(value === item3[1], 13); 44 | } 45 | 46 | console.assert(lru1.peek(item3[0]) === item3[1], 14); 47 | 48 | lru3.set(...item1).set(...item2); 49 | 50 | await sleep(100); 51 | lru3.set(...item3); 52 | await sleep(10); 53 | console.assert(JSON.stringify([...lru3]) === JSON.stringify([item2, item3]), 15); 54 | 55 | await sleep(150); 56 | lru3.set(...item1).set(...item3); 57 | await sleep(10); 58 | console.assert(JSON.stringify([...lru3]) === JSON.stringify([item3, item1]), 16); 59 | 60 | await sleep(250); 61 | console.assert(JSON.stringify([...lru3]) === '[]', 17); 62 | lru3.set(...item1).set(...item2); 63 | 64 | await sleep(50); 65 | lru3.set(...item3); 66 | 67 | await sleep(0); 68 | lru3.set(...item1); 69 | const lur4 = new LRU({maxAge: 100}); 70 | console.assert(lur4.peek('any') === void 0, 18); 71 | lur4.set(...item1); 72 | 73 | await sleep(50); 74 | lur4.set(...item2).set(...item3); 75 | 76 | })(); 77 | --------------------------------------------------------------------------------