-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathsimple_js_cache_idb.js
187 lines (158 loc) · 6.05 KB
/
simple_js_cache_idb.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
/*
N. Frick, 2021
Purpose: a fast, simple dual-layer cache
Level l. Map() Object
Level 2. Browser storage (uses idb-keyval)
Example usage:
This is based on promises, so call using the "consuming code" mode (using .then):
setCacheStorageP(myKey,myObject).then(function() { console.log('saved');})
getCacheStorageP(myKey).then(function(theObj) { myItem = theObj; })
Implementation comments: Each routine has a synchronous and async part
Note: this is intended for use as a ES module, so rename extension if you want.
*/
import { get as idbGet, set as idbSet, getMany as idbGetMany,
setMany as idbSetMany, delMany as idbDelMany, update as idbUpdate,
entries as idbEntries, keys as idbKeys, values as idbValues,
upsert as idbUpsert } from 'idb-keyval.js';
var gAppCacheNF = new Map(); // Large Global var to hold cache
export function setCacheStorageP (keyName,theObject) {
return new Promise( (resolve,reject) => {
gAppCacheNF.set(keyName,theObject);
idbSet(keyName,theObject).then(
() => { resolve(); } )
.catch((err)=> {reject(err);} );
});
}
export function setManyCacheStorageP (keyValuePairsAry) { // [[key1,value1],[key2,value2],...]
return new Promise( (resolve,reject) => {
let l = keyValuePairsAry.length;
for (let i=0;i<l;i++) {
gAppCacheNF.set(keyValuePairsAry[i][0],keyValuePairsAry[i][1]);
}
idbSetMany(keyValuePairsAry).then(
() => { resolve(); } )
.catch((err)=> {reject(err);} );
});
}
export function getCacheStorageP (keyName) {
return new Promise( (resolve,reject) => {
if (gAppCacheNF.has(keyName))
{ var rs = gAppCacheNF.get(keyName);
if (Object.keys(rs).length > 0) {
resolve(rs);
}
}
idbGet(keyName).then(
(rs) => { if (rs == null) rs = {};
if (Object.keys(rs).length > 0) {
//console.log('setting gAppCacheNF using '+ keyName +'...');
gAppCacheNF.set(keyName,rs); // push to faster (global) var
}
resolve(rs); }
);
});
}
export function getManyCacheStorageP(keyNameAry) {
/* in order to not complicate things, we take the philosophical approach that
this won't be called, except at the start of the lifecycle of the JS routine,
and so we go straight to the idb and get whatever is there and use it
(rather than checking the gAppCacheNF map) */
return new Promise( (resolve,reject) => {
idbGetMany(keyNameAry).then((valAry) =>
{ let l = keyNameAry.length;
for (let i=0;i<l;i++) {
let val2Set = (valAry[i] || {}); // mask undefined values as empty objects
if (Object.keys(val2Set).length > 0) {
gAppCacheNF.set(keyNameAry[i],val2Set);
}
}
resolve(valAry); // send it back
} ).catch((err) => {reject(err);});
});
}
// removes item from cache by keyName
export function deleteCacheStorageP (keyName) {
return new Promise( (resolve,reject) => {
gAppCacheNF.delete(keyName);
var keyAry = []; keyAry.push(keyName);
idbDelMany(keyAry).then(
() => { resolve(); } )
.catch((err)=> {reject(err);} );
});
}
// deletes from existing cache item props that are in specObject
export function deletePropObjCacheStorageP (keyName,specObject) {
return new Promise( (resolve,reject) => {
if (gAppCacheNF.has(keyName))
{ var rs = gAppCacheNF.get(keyName);
// delete props given in specObject
Object.keys(specObject).forEach( (key) => {
try { delete rs[key] } catch (ex) { }
});
gAppCacheNF.set(keyName,rs);
}
idbUpsert(keyName,(rs)=>{ if (rs == null) rs = {};
Object.keys(specObject).forEach( (key) => {
try { delete rs[key] } catch (ex) { }
}); return rs;},specObject);
resolve();
});
}
// deletes from existing cache item props that are specified by propNameArray
export function deletePropNameCacheStorageP (keyName,propNameArray) {
return new Promise((resolve,reject) => {
if (gAppCacheNF.has(keyName))
{ var rs = gAppCacheNF.get(keyName);
// delete
if (Array.isArray(propNameArray)) {
var l = propNameArray.length;
for (var i=0;i<l;i++) {
try { delete rs[propNameArray[i]] } catch (ex) { }
}
}
gAppCacheNF.set(keyName,rs); //save
}
idbUpsert(keyName,(rs)=>{ if (rs == null) rs = {};
if (Array.isArray(propNameArray)) {
var l = propNameArray.length;
for (var i=0;i<l;i++) {
try { delete rs[propNameArray[i]] } catch (ex) { }
}
}; return rs;},specObject);
resolve();
});
}
// a general utility function
function appendCallbackArgs(callback, ...extraArgs) {
return (...args) => callback(...args, ...extraArgs); // suggested by Jake A.
}
// combines(overwrites) existing cache object props with new object props
export function upsertCacheStorageP (keyName,theMixObject) {
return new Promise( (resolve,reject) => {
if (gAppCacheNF.has(keyName))
{ var rs = gAppCacheNF.get(keyName);
var rsm = Object.assign(rs,theMixObject);
gAppCacheNF.set(keyName,rsm); // combine and save
}
var idbUpdateF = (v,mxObj)=>(Object.assign({},(v || {}),mxObj));
idbUpdate(keyName,appendCallbackArgs(idbUpdateF,theMixObject) ) // use appendCallbackArgs wrapper
.then( ()=>{ resolve(); } );
});
}
// quick test for browser support
export function testCacheStorageP () {
return new Promise( (resolve,reject) => {
idbSetMany([
['testCache', 1]
])
.then(() => {resolve();})
.catch((err)=>{reject(err);});
});
}
// synch read from cache
export function getCacheStorageL1 (keyName) {
if (gAppCacheNF.has(keyName))
return gAppCacheNF.get(keyName);
else
return {};
}