├── .gitignore ├── API.md ├── LICENSE ├── README.md ├── example └── nextjs │ ├── package.json │ └── pages │ └── index.js ├── package.json ├── rollup.config.js └── src ├── index.js └── index.ts /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | node_modules 5 | 6 | # builds 7 | build 8 | dist 9 | 10 | package-lock.json 11 | 12 | /example/nextjs/node_modules 13 | /example/nextjs/.next 14 | /example/nextjs/package-lock.json 15 | -------------------------------------------------------------------------------- /API.md: -------------------------------------------------------------------------------- 1 | # Local Async Storage API 2 | 3 | **Table of Contents** 4 | 5 | - [setItem](#setItem) 6 | - [getItem](#getItem) 7 | - [removeItem](#removeItem) 8 | - [clearStorage](#clearStorage) 9 | - [getKeys](#getKeys) 10 | - [setMultiple](#setMultiple) 11 | - [getMultiple](#getMultiple) 12 | - [removeMultiple](#removeMultiple) 13 | 14 | ## `setItem` 15 | 16 | Sets a `value` for a `key` and invokes a (optional) callback once completed. 17 | 18 | **Method**: 19 | 20 | ```js 21 | static setItem(key, value, [callback]) 22 | ``` 23 | 24 | **Return**: 25 | 26 | A `Promise` object. 27 | 28 | **Parameters:** 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 |
NAMETYPEREQUIREDDESCRIPTION
keystringYesKey of the item to set.
valuestringYesValue to set for the key.
callback?(error: ?Error) => voidNoFunction that will be called with any error.
60 | 61 | **Example**: 62 | 63 | ```js 64 | setItem = async () => { 65 | try { 66 | await AsyncLocalStorage.setItem('@key', 'value') 67 | } catch(e) { 68 | // error 69 | } 70 | } 71 | ``` 72 | 73 | ## `getItem` 74 | 75 | Fetches an item for a given key and invokes (optional) callback once completed. 76 | 77 | **Method**: 78 | 79 | ```js 80 | static getItem(key, [callback]) 81 | ``` 82 | 83 | **Return**: 84 | 85 | A `Promise` with item, if exists, `null` otherwise. 86 | 87 | **Parameters:** 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 |
NAMETYPEREQUIREDDESCRIPTION
keystringYesKey of the item to fetch.
callback?(error: ?Error, result: ?string) => voidNoFunction that will be called with a result if found or any error.
113 | 114 | **Example**: 115 | 116 | ```js 117 | getItem = async () => { 118 | let value 119 | 120 | try { 121 | value = await AsyncLocalStorage.getItem('@key') 122 | } catch(e) { 123 | // error 124 | } 125 | 126 | console.log(value) 127 | 128 | /* 129 | output: 130 | value 131 | */ 132 | } 133 | ``` 134 | 135 | ## `removeItem` 136 | 137 | Removes an item for a `key`, and invokes (optional) callback once completed. 138 | 139 | **Method**: 140 | 141 | ```js 142 | static removeItem(key, [callback]) 143 | ``` 144 | 145 | **Return**: 146 | 147 | A `Promise` object. 148 | 149 | **Parameters:** 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 |
NAMETYPEREQUIREDDESCRIPTION
keystringYesKey of the item to remove.
callback?(error: ?Error) => voidNoFunction that will be called with any error.
175 | 176 | **Example**: 177 | 178 | ```js 179 | removeItem = async () => { 180 | try { 181 | await AsyncLocalStorage.removeItem('@key') 182 | } catch(e) { 183 | // error 184 | } 185 | } 186 | ``` 187 | 188 | ## `clearStorage` 189 | 190 | Erases all `AsyncLocalStorage` for all clients, libraries, etc. You probably don't want to call this; use [removeItem](#removeItem) or [removeMultiple](#removeMultiple) to clear only your app's keys. 191 | 192 | **Method**: 193 | 194 | ```js 195 | static clearStorage([callback]) 196 | ``` 197 | 198 | **Return**: 199 | 200 | A `Promise` object. 201 | 202 | **Parameters:** 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 |
NAMETYPEREQUIREDDESCRIPTION
callback?(error: ?Error) => voidNoFunction that will be called with any error.
222 | 223 | **Example**: 224 | 225 | ```js 226 | clearStorage = async () => { 227 | try { 228 | await AsyncLocalStorage.clearStorage() 229 | } catch(e) { 230 | // error 231 | } 232 | } 233 | ``` 234 | 235 | ## `getKeys` 236 | 237 | Returns all keys known to your App, for all callers, libraries, etc. Once completed, invokes (optional) callback with errors (if any) and array of keys. 238 | 239 | **Method**: 240 | 241 | ```js 242 | static getKeys([callback]) 243 | ``` 244 | 245 | **Return**: 246 | 247 | A `Promise` object. 248 | 249 | **Parameters:** 250 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | 259 | 260 | 261 | 262 | 263 | 264 | 265 | 266 | 267 | 268 |
NAMETYPEREQUIREDDESCRIPTION
callback?(error: ?Error, keys: ?Array) => voidNoFunction that will be called with all keys found and any error.
269 | 270 | **Example**: 271 | 272 | ```js 273 | getKeys = async () => { 274 | let keys = [] 275 | 276 | try { 277 | keys = await AsyncLocalStorage.getKeys() 278 | } catch(e) { 279 | // error 280 | } 281 | 282 | console.log(keys) 283 | 284 | /* 285 | output: 286 | ["@key"] 287 | */ 288 | } 289 | ``` 290 | 291 | ## `setMultiple` 292 | 293 | Stores multiple key-value pairs in a batch. Once completed, `callback` with any errors will be called. 294 | 295 | **Method**: 296 | 297 | ```js 298 | static setMultiple(keyValuePairs, [callback]) 299 | ``` 300 | 301 | **Return**: 302 | 303 | A `Promise` object. 304 | 305 | **Parameters:** 306 | 307 | 308 | 309 | 310 | 311 | 312 | 313 | 314 | 315 | 316 | 317 | 318 | 319 | 320 | 321 | 322 | 323 | 324 | 325 | 326 | 327 | 328 | 329 | 330 |
NAMETYPEREQUIREDDESCRIPTION
keyValuePairsArrayYesArray of key-value object for the items to set.
callback?(errors: ?Array) => voidNoFunction that will be called with an array of any key-specific errors found.
331 | 332 | **Example**: 333 | 334 | ```js 335 | setMultiple = async () => { 336 | const firstPair = { key1: 'hello1' }; 337 | const secondPair = { key2: 'hello2' }; 338 | 339 | try { 340 | await AsyncLocalStorage.setMultiple([value1, value2]) 341 | } catch(e) { 342 | // error 343 | } 344 | } 345 | ``` 346 | 347 | ## `getMultiple` 348 | 349 | Fetches multiple key-value pairs for given array of `keys` in a batch. Once completed, invokes `callback` with errors (if any) and results. 350 | 351 | **Method**: 352 | 353 | ```js 354 | static getMultiple(keys, [callback]) 355 | ``` 356 | 357 | **Return**: 358 | 359 | A `Promise` of array with coresponding key-value pairs found, stored as `{key: value}` array. 360 | 361 | **Parameters:** 362 | 363 | 364 | 365 | 366 | 367 | 368 | 369 | 370 | 371 | 372 | 373 | 374 | 375 | 376 | 377 | 378 | 379 | 380 | 381 | 382 | 383 | 384 | 385 | 386 |
NAMETYPEREQUIREDDESCRIPTION
keysArrayYesArray of key for the items to get.
callback?(errors: ?Array, result: ?Array) => voidNoFunction that will be called with a key-value array of the results, plus an array of any key-specific errors found.
387 | 388 | **Example**: 389 | 390 | ```js 391 | getMultiple = async () => { 392 | let items 393 | 394 | try { 395 | items = await AsyncLocalStorage.getMultiple(['@key1', '@key2']) 396 | } catch(e) { 397 | // error 398 | } 399 | 400 | console.log(items) 401 | 402 | /* 403 | output: 404 | [ 405 | {key1: "hello1"}, 406 | {key2: "hello2"} 407 | ] 408 | */ 409 | } 410 | ``` 411 | 412 | ## `removeMultiple` 413 | 414 | Delete multiple key-value entries for given array of `keys` in a batch. Once completed, invokes a `callback` with errors (if any). 415 | 416 | **Method**: 417 | 418 | ```js 419 | static removeMultiple(keys, [callback]) 420 | ``` 421 | 422 | **Return**: 423 | 424 | A `Promise` object. 425 | 426 | **Parameters:** 427 | 428 | 429 | 430 | 431 | 432 | 433 | 434 | 435 | 436 | 437 | 438 | 439 | 440 | 441 | 442 | 443 | 444 | 445 | 446 | 447 | 448 | 449 | 450 | 451 |
NAMETYPEREQUIREDDESCRIPTION
keysArrayYesArray of key for the items to delete.
callback?(errors: ?Array) => voidNoFunction that will be called an array of any key-specific errors found.
452 | 453 | **Example**: 454 | 455 | ```js 456 | removeFew = async () => { 457 | const keys = ['key1', 'key1'] 458 | 459 | try { 460 | await AsyncLocalStorage.removeMultiple(keys) 461 | } catch(e) { 462 | // error 463 | } 464 | } 465 | ``` 466 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2020 Create Next App 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 6 | 7 | The above copyright notice and this permission notice shall be included in 8 | all copies or substantial portions of the Software. 9 | 10 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 11 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 12 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 13 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 14 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 15 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 16 | THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # async-local-storage 2 | 3 | AsyncLocalStorage is an unencrypted, asynchronous, persistent, key-value storage system that is global to the app in web browser. It should be used instead of LocalStorage. 4 | 5 | [![NPM](https://img.shields.io/npm/v/async-local-storage.svg)](https://www.npmjs.com/package/@createnextapp/async-local-storage) ![npm bundle size](https://img.shields.io/bundlephobia/min/@createnextapp/async-local-storage) 6 | 7 | ## ❓ Why AsyncLocalStorage instead of LocalStorage? 8 | 9 | **Cons of LocalStorage** 10 | 11 | LocalStorage is synchronous, each local storage operation you run will be one-at-a-time. For complex applications this is a big no-no as it'll slow down your app's runtime. 12 | 13 | **Pros of AsyncLocalStorage** 14 | 15 | AsyncLocalStorage is asynchronous, each local async storage operation you run will be multi-at-a-time. It'll speed up your app's runtime. 16 | 17 | The AsyncLocalStorage JavaScript code is a facade that provides [a clear JavaScript API](./API.md), real Error objects, and non-multi functions. Each method in the API returns a Promise object. 18 | 19 | ## 🔧 Install 20 | 21 | async-local-storage is available on npm. It can be installed with the following command: 22 | 23 | ``` 24 | npm install --save @createnextapp/async-local-storage 25 | ``` 26 | 27 | async-local-storage is available on yarn as well. It can be installed with the following command: 28 | 29 | ``` 30 | yarn add @createnextapp/async-local-storage 31 | ``` 32 | 33 | ## 💡 Usage 34 | 35 | To learn more how to use async-local-storage: 36 | 37 | * [API Documentation](./API.md) 38 | 39 | ### Import 40 | 41 | ```js 42 | import AsyncLocalStorage from '@createnextapp/async-local-storage' 43 | ``` 44 | 45 | ### Store data 46 | 47 | ```js 48 | storeData = async () => { 49 | try { 50 | await AsyncLocalStorage.setItem('@key', 'value') 51 | } catch(e) { 52 | // error 53 | } 54 | } 55 | ``` 56 | 57 | ### Read data 58 | 59 | ```js 60 | readData = async () => { 61 | let data 62 | 63 | try { 64 | data = await AsyncLocalStorage.getItem('@key') 65 | } catch(e) { 66 | // error 67 | } 68 | 69 | console.log(data) 70 | 71 | /* 72 | output: 73 | value 74 | */ 75 | } 76 | ``` 77 | 78 | ## 💖 Wrap Up 79 | 80 | If you think any of the `async-local-storage` can be improved, please do open a PR with any updates and submit any issues. Also, I will continue to improve this, so you might want to watch/star this repository to revisit. 81 | 82 | ## 🌟 Contribution 83 | 84 | We'd love to have your helping hand on contributions to `async-local-storage` by forking and sending a pull request! 85 | 86 | Your contributions are heartily ♡ welcome, recognized and appreciated. (✿◠‿◠) 87 | 88 | How to contribute: 89 | 90 | - Open pull request with improvements 91 | - Discuss ideas in issues 92 | - Spread the word 93 | - Reach out with any feedback 94 | 95 | ## ⚖️ License 96 | 97 | The MIT License [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) 98 | -------------------------------------------------------------------------------- /example/nextjs/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "nextjs", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "dev": "next", 8 | "build": "next build", 9 | "start": "next start" 10 | }, 11 | "keywords": [], 12 | "author": "", 13 | "license": "ISC", 14 | "dependencies": { 15 | "next": "^9.3.5", 16 | "react": "^16.13.1", 17 | "react-dom": "^16.13.1" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /example/nextjs/pages/index.js: -------------------------------------------------------------------------------- 1 | import AsyncLocalStorage from '@createnextapp/async-local-storage' 2 | 3 | export default function Index() { 4 | const setItem = async () => { 5 | try { 6 | await AsyncLocalStorage.setItem('@me', 'Bunlong') 7 | } catch (e) { 8 | // read error 9 | } 10 | 11 | console.log('Set.') 12 | } 13 | 14 | const getItem = async () => { 15 | let item 16 | 17 | try { 18 | item = await AsyncLocalStorage.getItem('@me') 19 | } catch(e) { 20 | // read error 21 | } 22 | 23 | console.log(item) 24 | } 25 | 26 | const removeItem = async () => { 27 | try { 28 | await AsyncLocalStorage.removeItem('@me') 29 | } catch(e) { 30 | // remove error 31 | } 32 | 33 | console.log('Remove.') 34 | } 35 | 36 | const clearStorage = async () => { 37 | try { 38 | await AsyncLocalStorage.clearStorage() 39 | } catch(e) { 40 | // remove error 41 | } 42 | 43 | console.log('Remove.') 44 | } 45 | 46 | const getKeys = async () => { 47 | let keys = [] 48 | 49 | try { 50 | keys = await AsyncLocalStorage.getKeys() 51 | } catch(e) { 52 | // read key error 53 | } 54 | 55 | console.log(keys) 56 | } 57 | 58 | const setMultiple = async () => { 59 | const value1 = { key1: 'hello1' }; 60 | const value2 = { key2: 'hello2' }; 61 | 62 | try { 63 | await AsyncLocalStorage.setMultiple([value1, value2]) 64 | } catch(e) { 65 | //save error 66 | } 67 | 68 | console.log("Done.") 69 | } 70 | 71 | const getMultiple = async () => { 72 | let values 73 | 74 | try { 75 | values = await AsyncLocalStorage.getMultiple(['key1', 'key2']) 76 | } catch(e) { 77 | // read error 78 | } 79 | 80 | console.log(values) 81 | } 82 | 83 | const removeMultiple = async () => { 84 | const keys = ['key1', 'key2'] 85 | 86 | try { 87 | await AsyncLocalStorage.removeMultiple(keys) 88 | } catch(e) { 89 | // remove error 90 | } 91 | 92 | console.log('Done') 93 | } 94 | 95 | return ( 96 | <> 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | ); 107 | } 108 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@createnextapp/async-local-storage", 3 | "version": "1.0.1", 4 | "description": "AsyncLocalStorage is an unencrypted, asynchronous, persistent, key-value storage system that is global to the app in web browser. It should be used instead of LocalStorage.", 5 | "main": "dist/async-local-storage.js", 6 | "module": "dist/async-local-storage.es.js", 7 | "jsnext:main": "dist/async-local-storage.es.js", 8 | "scripts": { 9 | "build": "rollup -c", 10 | "dev": "rollup -c -w", 11 | "test": "node test/test.js", 12 | "pretest": "npm run build" 13 | }, 14 | "author": "Bunlong ", 15 | "homepage": "https://github.com/CreateNextApp/async-local-storage.git", 16 | "repository": "https://github.com/CreateNextApp/async-local-storage.git", 17 | "bugs": { 18 | "url": "https://github.com/CreateNextApp/async-local-storage/issues" 19 | }, 20 | "license": "MIT", 21 | "devDependencies": { 22 | "@rollup/plugin-commonjs": "^11.1.0", 23 | "@rollup/plugin-node-resolve": "^7.1.3", 24 | "rollup": "^2.6.1", 25 | "rollup-plugin-terser": "^5.3.0" 26 | }, 27 | "files": [ 28 | "dist" 29 | ], 30 | "keywords": [ 31 | "localstorage", 32 | "local-storage", 33 | "asyncstorage", 34 | "asynclocalstorage", 35 | "local-async-storage", 36 | "storage", 37 | "react", 38 | "reactjs", 39 | "vue", 40 | "vuejs", 41 | "angular", 42 | "angularjs", 43 | "web", 44 | "app" 45 | ] 46 | } 47 | -------------------------------------------------------------------------------- /rollup.config.js: -------------------------------------------------------------------------------- 1 | import resolve from '@rollup/plugin-node-resolve'; 2 | import commonjs from '@rollup/plugin-commonjs'; 3 | import { terser } from "rollup-plugin-terser"; 4 | import pkg from './package.json'; 5 | 6 | export default [ 7 | { 8 | input: 'src/index.js', 9 | output: [ 10 | { file: pkg.main, format: 'cjs' }, 11 | { file: pkg.module, format: 'es' } 12 | ], 13 | plugins: [ 14 | resolve(), 15 | commonjs(), 16 | terser() 17 | ] 18 | } 19 | ]; 20 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | const createPromise = (getValue, callback) => { 2 | return new Promise((resolve, reject) => { 3 | try { 4 | const value = getValue(); 5 | if (callback) { 6 | callback(null, value); 7 | } 8 | resolve(value); 9 | } catch (err) { 10 | if (callback) { 11 | callback(err); 12 | } 13 | reject(err); 14 | } 15 | }); 16 | }; 17 | 18 | const createPromiseAll = (promises, callback, processResult) => { 19 | return Promise.all(promises).then( 20 | result => { 21 | const value = processResult ? processResult(result) : null; 22 | callback && callback(null, value); 23 | return Promise.resolve(value); 24 | }, 25 | errors => { 26 | callback && callback(errors); 27 | return Promise.reject(errors); 28 | } 29 | ); 30 | }; 31 | 32 | export default class AsyncLocalStorage { 33 | /** 34 | * Sets value for key. 35 | */ 36 | static setItem(key, value, callback) { 37 | return createPromise(() => { 38 | window.localStorage.setItem(key, value); 39 | }, callback); 40 | } 41 | 42 | /** 43 | * Fetches key value. 44 | */ 45 | static getItem(key, callback) { 46 | return createPromise(() => { 47 | return window.localStorage.getItem(key); 48 | }, callback); 49 | } 50 | 51 | /** 52 | * Removes a key. 53 | */ 54 | static removeItem(key, callback) { 55 | return createPromise(() => { 56 | return window.localStorage.removeItem(key); 57 | }, callback); 58 | } 59 | 60 | /** 61 | * Erases *all* AsyncLocalStorage for the domain. 62 | */ 63 | static clearStorage(callback) { 64 | return createPromise(() => { 65 | window.localStorage.clear(); 66 | }, callback); 67 | } 68 | 69 | /** 70 | * Gets *all* keys known to the app, for all callers, libraries, etc. 71 | */ 72 | static getKeys(callback) { 73 | return createPromise(() => { 74 | const numberOfKeys = window.localStorage.length; 75 | const keys = []; 76 | for (let i = 0; i < numberOfKeys; i += 1) { 77 | const key = window.localStorage.key(i); 78 | keys.push(key); 79 | } 80 | return keys; 81 | }, callback); 82 | } 83 | 84 | /** 85 | * Takes an array of key-value json pairs. 86 | * setMultifple([{k1: 'val1'}, {k2: 'val2'}]) 87 | */ 88 | static setMultiple(keyValuePairs, callback) { 89 | const promises = keyValuePairs.map(item => { for(const key in item) AsyncLocalStorage.setItem(key, item[key]) }); 90 | return createPromiseAll(promises, callback); 91 | } 92 | 93 | /** 94 | * getMultiple resolves to an array of key-value pair objects that matches the 95 | * input format of getMultiple. 96 | * 97 | * getMultiple(['k1', 'k2']) -> [{k1: 'val1'}, {'k2': 'val2'}] 98 | */ 99 | static getMultiple(keys, callback) { 100 | const promises = keys.map(key => AsyncLocalStorage.getItem(key)); 101 | const processResult = result => result.map((value, i) => { 102 | const json = {} 103 | json[keys[i]] = value 104 | return json 105 | }); 106 | return createPromiseAll(promises, callback, processResult); 107 | } 108 | 109 | /** 110 | * Delete all the keys in the keys array. 111 | */ 112 | static removeMultiple(keys, callback) { 113 | const promises = keys.map(key => AsyncLocalStorage.removeItem(key)); 114 | return createPromiseAll(promises, callback); 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | import merge from 'deep-assign'; 2 | 3 | const mergeLocalStorageItem = (key, value) => { 4 | const oldValue = window.localStorage.getItem(key); 5 | const oldObject = JSON.parse(oldValue); 6 | const newObject = JSON.parse(value); 7 | const nextValue = JSON.stringify(merge({}, oldObject, newObject)); 8 | window.localStorage.setItem(key, nextValue); 9 | }; 10 | 11 | const createPromise = (getValue, callback): Promise<*> => { 12 | return new Promise((resolve, reject) => { 13 | try { 14 | const value = getValue(); 15 | if (callback) { 16 | callback(null, value); 17 | } 18 | resolve(value); 19 | } catch (err) { 20 | if (callback) { 21 | callback(err); 22 | } 23 | reject(err); 24 | } 25 | }); 26 | }; 27 | 28 | const createPromiseAll = (promises, callback, processResult): Promise<*> => { 29 | return Promise.all(promises).then( 30 | result => { 31 | const value = processResult ? processResult(result) : null; 32 | callback && callback(null, value); 33 | return Promise.resolve(value); 34 | }, 35 | errors => { 36 | callback && callback(errors); 37 | return Promise.reject(errors); 38 | } 39 | ); 40 | }; 41 | 42 | export default class AsyncLocalStorage { 43 | 44 | /** 45 | * Fetches `key` value. 46 | */ 47 | static getItem(key: string, callback?: Function): Promise<*> { 48 | return createPromise(() => { 49 | return window.localStorage.getItem(key); 50 | }, callback); 51 | } 52 | 53 | /** 54 | * Sets `value` for `key`. 55 | */ 56 | static setItem(key: string, value: string, callback?: Function): Promise<*> { 57 | return createPromise(() => { 58 | window.localStorage.setItem(key, value); 59 | }, callback); 60 | } 61 | 62 | /** 63 | * Removes a `key` 64 | */ 65 | static removeItem(key: string, callback?: Function): Promise<*> { 66 | return createPromise(() => { 67 | return window.localStorage.removeItem(key); 68 | }, callback); 69 | } 70 | 71 | /** 72 | * Merges existing value with input value, assuming they are stringified JSON. 73 | */ 74 | static mergeItem(key: string, value: string, callback?: Function): Promise<*> { 75 | return createPromise(() => { 76 | mergeLocalStorageItem(key, value); 77 | }, callback); 78 | } 79 | 80 | /** 81 | * Erases *all* AsyncLocalStorage for the domain. 82 | */ 83 | static clear(callback?: Function): Promise<*> { 84 | return createPromise(() => { 85 | window.localStorage.clear(); 86 | }, callback); 87 | } 88 | 89 | /** 90 | * Gets *all* keys known to the app, for all callers, libraries, etc. 91 | */ 92 | static getAllKeys(callback?: Function): Promise<*> { 93 | return createPromise(() => { 94 | const numberOfKeys = window.localStorage.length; 95 | const keys = []; 96 | for (let i = 0; i < numberOfKeys; i += 1) { 97 | const key = window.localStorage.key(i); 98 | keys.push(key); 99 | } 100 | return keys; 101 | }, callback); 102 | } 103 | 104 | /** 105 | * (stub) Flushes any pending requests using a single batch call to get the data. 106 | */ 107 | static flushGetRequests() {} 108 | 109 | /** 110 | * multiGet resolves to an array of key-value pair arrays that matches the 111 | * input format of multiSet. 112 | * 113 | * multiGet(['k1', 'k2']) -> [['k1', 'val1'], ['k2', 'val2']] 114 | */ 115 | static multiGet(keys: Array, callback?: Function): Promise<*> { 116 | const promises = keys.map(key => AsyncLocalStorage.getItem(key)); 117 | const processResult = result => result.map((value, i) => [keys[i], value]); 118 | return createPromiseAll(promises, callback, processResult); 119 | } 120 | 121 | /** 122 | * Takes an array of key-value array pairs. 123 | * multiSet([['k1', 'val1'], ['k2', 'val2']]) 124 | */ 125 | static multiSet(keyValuePairs: Array>, callback?: Function): Promise<*> { 126 | const promises = keyValuePairs.map(item => AsyncLocalStorage.setItem(item[0], item[1])); 127 | return createPromiseAll(promises, callback); 128 | } 129 | 130 | /** 131 | * Delete all the keys in the `keys` array. 132 | */ 133 | static multiRemove(keys: Array, callback?: Function): Promise<*> { 134 | const promises = keys.map(key => AsyncLocalStorage.removeItem(key)); 135 | return createPromiseAll(promises, callback); 136 | } 137 | 138 | /** 139 | * Takes an array of key-value array pairs and merges them with existing 140 | * values, assuming they are stringified JSON. 141 | * 142 | * multiMerge([['k1', 'val1'], ['k2', 'val2']]) 143 | */ 144 | static multiMerge(keyValuePairs: Array>, callback?: Function): Promise<*> { 145 | const promises = keyValuePairs.map(item => AsyncLocalStorage.mergeItem(item[0], item[1])); 146 | return createPromiseAll(promises, callback); 147 | } 148 | } --------------------------------------------------------------------------------