@arcgis/core
Version:
ArcGIS Maps SDK for JavaScript: A complete 2D and 3D mapping and data visualization API
6 lines (5 loc) • 4.43 kB
JavaScript
/*
All material copyright ESRI, All Rights Reserved, unless otherwise specified.
See https://js.arcgis.com/4.32/esri/copyright.txt for details.
*/
import{ByteSizeUnit as e}from"../../../../core/ByteSizeUnit.js";import t from"../../../../core/Error.js";import{onAbort as s,throwIfAborted as r}from"../../../../core/promiseUtils.js";const o=14;class i{constructor(t,s,r=o){this._version=r,this._db=null,this._quotaReductionPromise=null,this._gcCounter=0,this._hit=0,this._miss=0,this._destroyed=!1,this.gcFrequency=50,this.maxByteSize=e.GIGABYTES,this.quotaReductionFactor=.2,this._dbName=t,this._storeName=s}init(){return Promise.resolve().then((()=>{const e=indexedDB.open(this._dbName,this._version);return e.onupgradeneeded=t=>{const s=e.result,r=e.transaction,o=s.objectStoreNames.contains(this._storeName)?r.objectStore(this._storeName):s.createObjectStore(this._storeName),i=s.objectStoreNames.contains("last_access")?r.objectStore("last_access"):s.createObjectStore("last_access");i.indexNames.contains("date")||i.createIndex("date","date",{unique:!1}),i.indexNames.contains("byteSize")||i.createIndex("byteSize","byteSize",{unique:!1}),t.oldVersion<this._version&&(o.clear(),i.clear())},c(e)})).then((e=>{this._destroyed?e.close():this._db=e}))}destroy(){this._db&&(this._db.close(),this._db=null),this._destroyed=!0}get initialized(){return null!=this._db}getHitRate(){return this._hit/(this._hit+this._miss)}put(e,s){if(null==this._db)return Promise.reject(new t("indexedb:not-initialized","IndexedDB Cache is not initialized"));return(null!=this._quotaReductionPromise?this._quotaReductionPromise:Promise.resolve()).then((()=>this._put(e,s))).catch((t=>{if(t&&"QuotaExceededError"===t.name)return null==this._quotaReductionPromise&&(this._quotaReductionPromise=this._getCacheSize().then((e=>this._removeLeastRecentlyAccessed(s.byteSize+Math.ceil(e*this.quotaReductionFactor)))),this._quotaReductionPromise.then((()=>this._quotaReductionPromise=null),(()=>this._quotaReductionPromise=null))),this._quotaReductionPromise.then((()=>this._put(e,s)));throw t})).then((()=>{this._gcCounter--,this._gcCounter<0&&null!=this._db&&(this._gcCounter=this.gcFrequency,this._getCacheSize().then((e=>this._removeLeastRecentlyAccessed(e-this.maxByteSize))))}))}get(e,t){const o=this._db;if(null==o)return Promise.resolve(void 0);let i=null;return Promise.resolve().then((()=>{const r=o.transaction(this._storeName,"readonly");i=s(t,(()=>{r.abort()}));return c(r.objectStore(this._storeName).get(e))})).then((t=>{if(null==t)++this._miss;else if(this._db){++this._hit;this._db.transaction("last_access","readwrite").objectStore("last_access").put({date:Date.now(),byteSize:t.byteSize},e)}return null!=i&&i.remove(),t})).catch((()=>{++this._miss,r(t),null!=i&&i.remove()}))}remove(e){const t=this._db;return null==t?Promise.resolve():Promise.resolve().then((async()=>{const s=t.transaction([this._storeName,"last_access"],"readwrite"),r=s.objectStore(this._storeName),o=s.objectStore("last_access"),i=r.delete(e),a=o.delete(e);await Promise.all([c(i),c(a),n(s)])}))}_put(e,t){const s=this._db;if(null==s)return Promise.resolve();const r=s.transaction([this._storeName,"last_access"],"readwrite"),o=r.objectStore(this._storeName),i=r.objectStore("last_access"),a=o.put(t,e),l=i.put({date:Date.now(),byteSize:t.byteSize},e);return Promise.all([c(a),c(l),n(r)])}_removeLeastRecentlyAccessed(e){if(e<=0||!this._db)return Promise.resolve();const t=this._db.transaction([this._storeName,"last_access"],"readwrite"),s=t.objectStore(this._storeName),r=t.objectStore("last_access");let o=0;const i=r.index("date").openCursor(null,"next");return i.onsuccess=()=>{const t=i.result;null!=t&&(null!=t.value.byteSize&&(o+=t.value.byteSize),s.delete(t.primaryKey),r.delete(t.primaryKey),o<e&&t.continue())},n(t)}_getCacheSize(){const e=this._db;if(null==e)return Promise.resolve(0);const t=e.transaction("last_access"),s=t.objectStore("last_access");let r=0;const o=s.index("byteSize").openKeyCursor();return o.onsuccess=()=>{const e=o.result;if(!e)return;const t=e.key;null!=t&&(r+=t),e.continue()},n(t).then((()=>r))}}function n(e){return new Promise(((t,s)=>{e.oncomplete=()=>t(),e.onerror=()=>s(e.error),e.onabort=()=>s(e.error)}))}function c(e){return new Promise(((t,s)=>{"done"===e.readyState?null!=e.error?s(e.error):t(e.result):(e.onsuccess=()=>t(e.result),e.onerror=()=>s(e.error))}))}export{i as IDBCache,c as whenRequest,n as whenTransaction};