inline > On Sep 1, 2017, at 1:03 AM, Isiah Meadows <isiahmead...@gmail.com> wrote: > > It'd solve a problem similarly to Kotlin's `by lazy { ... }` delegate, > .NET's `System.Lazy<T>`, Swift's `lazy var`, among many other > languages. It's very useful for lazy initialization [1], such as > lazily setting up a database, requesting a resource, among other > costly things. [2] > > How often do you start out with a class like this, where you have an > expensive resource you don't want to open right away? > > ```js > class Foo { > constructor() { > this._db = undefined > } > > _initDb() { > if (this._db) return this._db > return this._db = new Promise((resolve, reject) => { > // open a database connection > // set up whatever tables you need to > // etc. > }) > } > } > ```
lazy db-initialization is over-engineering and unnecessary. almost all applications i encounter can be designed more simply with explicit db-initialization during startup. the only problem that arises is when the user tries to access the db before db-initialization completes. my solution (for indexeddb) is to wrap every db-crud method with a deferred-callback that waits for db-initialization to complete (or a promise object as andreas mentioned). https://kaizhu256.github.io/node-db-lite/build..master..travis-ci.org/apidoc.html#apidoc.element.db-lite.storageDefer <https://kaizhu256.github.io/node-db-lite/build..master..travis-ci.org/apidoc.html#apidoc.element.db-lite.storageDefer> storageGetItem = function (key, onError) { /* * this function will get the item with the given key from storage */ defer({ action: 'getItem', key: key }, onError); } storageRemoveItem = function (key, onError) { /* * this function will remove the item with the given key from storage */ defer({ action: 'removeItem', key: key }, onError); } storageSetItem = function (key, value, onError) { /* * this function will set the item with the given key and value to storage */ defer({ action: 'setItem', key: key, value: value }, onError); } storageDefer = function (options, onError) { /* * this function will defer options.action until storage is ready */ var data, isDone, objectStore, onError2, request, tmp; onError = onError || function (error) { // validate no error occurred console.assert(!error, error); }; if (!storage) { deferList.push(function () { defer(options, onError); }); init(); return; } switch (modeJs) { case 'browser': onError2 = function () { /* istanbul ignore next */ if (isDone) { return; } isDone = true; onError( request && (request.error || request.transaction.error), data || request.result || '' ); }; switch (options.action) { case 'clear': case 'removeItem': case 'setItem': objectStore = storage .transaction(storageDir, 'readwrite') .objectStore(storageDir); break; default: objectStore = storage .transaction(storageDir, 'readonly') .objectStore(storageDir); } switch (options.action) { case 'clear': request = objectStore.clear(); break; case 'getItem': request = objectStore.get(String(options.key)); break; case 'keys': data = []; request = objectStore.openCursor(); request.onsuccess = function () { if (!request.result) { onError2(); return; } data.push(request.result.key); request.result.continue(); }; break; case 'length': request = objectStore.count(); break; case 'removeItem': request = objectStore.delete(String(options.key)); break; case 'setItem': request = objectStore.put(options.value, String(options.key)); break; } ['onabort', 'onerror', 'onsuccess'].forEach(function (handler) { request[handler] = request[handler] || onError2; }); // debug request local._debugStorageRequest = request; break; case 'node': switch (options.action) { case 'clear': child_process.spawnSync('rm -f ' + storage + '/*', { shell: true, stdio: ['ignore', 1, 2] }); setTimeout(onError); break; case 'getItem': fs.readFile( storage + '/' + encodeURIComponent(String(options.key)), 'utf8', // ignore error function (error, data) { onError(error && null, data || ''); } ); break; case 'keys': fs.readdir(storage, function (error, data) { onError(error, data && data.map(decodeURIComponent)); }); break; case 'length': fs.readdir(storage, function (error, data) { onError(error, data && data.length); }); break; case 'removeItem': fs.unlink( storage + '/' + encodeURIComponent(String(options.key)), // ignore error function () { onError(); } ); break; case 'setItem': tmp = os.tmpdir() + '/' + Date.now() + Math.random(); // save to tmp fs.writeFile(tmp, options.value, function (error) { // validate no error occurred console.assert(!error, error); // rename tmp to key fs.rename( tmp, storage + '/' + encodeURIComponent(String(options.key)), onError ); }); ... > Or maybe, a large lookup table that takes a while to build, and might > not even be used, so you don't want to do it on load? > > ```js > var table > > function initTable() { > if (table) return > table = new Array(10000) > // do some expensive calculations > } > ``` this is a textbook-example for using memoization as alexander mentioned. here's a real-world memoized file-server solution https://kaizhu256.github.io/node-utility2/build..master..travis-ci.org/apidoc.html#apidoc.element.utility2.middlewareFileServer <https://kaizhu256.github.io/node-utility2/build..master..travis-ci.org/apidoc.html#apidoc.element.utility2.middlewareFileServer> middlewareFileServer = function (request, response, nextMiddleware) { /* * this function will run the middleware that will serve files */ if (request.method !== 'GET' || local.modeJs === 'browser') { nextMiddleware(); return; } request.urlFile = (process.cwd() + request.urlParsed.pathname // security - disable parent directory lookup .replace((/.*\/\.\.\//g), '/')) // replace trailing '/' with '/index.html' .replace((/\/$/), '/index.html'); // serve file from cache local.taskCreateCached({ cacheDict: 'middlewareFileServer', key: request.urlFile // run background-task to re-cache file }, function (onError) { local.fs.readFile(request.urlFile, function (error, data) { onError(error, data && local.base64FromBuffer(data)); }); }, function (error, data) { // default to nextMiddleware if (error) { nextMiddleware(); return; } // init response-header content-type request.urlParsed.contentType = (/\.[^\.]*$/).exec(request.urlParsed.pathname); request.urlParsed.contentType = local.contentTypeDict[ request.urlParsed.contentType && request.urlParsed.contentType[0] ]; local.serverRespondHeadSet(request, response, null, { 'Content-Type': request.urlParsed.contentType }); // serve file from cache response.end(local.base64ToBuffer(data)); }); } taskCreateCached = function (options, onTask, onError) { /* * this function will * 1. if cache-hit, then call onError with cacheValue * 2. run onTask in background * 3. save onTask's result to cache * 4. if cache-miss, then call onError with onTask's result */ local.onNext(options, function (error, data) { switch (options.modeNext) { // 1. if cache-hit, then call onError with cacheValue case 1: // read cacheValue from memory-cache local.cacheDict[options.cacheDict] = local.cacheDict[options.cacheDict] || {}; options.cacheValue = local.cacheDict[options.cacheDict][options.key]; if (options.cacheValue) { // call onError with cacheValue options.modeCacheHit = true; onError(null, JSON.parse(options.cacheValue)); if (!options.modeCacheUpdate) { break; } } // run background-task with lower priority for cache-hit setTimeout(options.onNext, options.modeCacheHit && options.cacheTtl); break; // 2. run onTask in background case 2: local.taskCreate(options, onTask, options.onNext); break; default: // 3. save onTask's result to cache // JSON.stringify data to prevent side-effects on cache options.cacheValue = JSON.stringify(data); if (!error && options.cacheValue) { local.cacheDict[options.cacheDict][options.key] = options.cacheValue; } // 4. if cache-miss, then call onError with onTask's result if (!options.modeCacheHit) { onError(error, options.cacheValue && JSON.parse(options.cacheValue)); } (options.onCacheWrite || local.nop)(); break; } }); options.modeNext = 0; options.onNext(); }
_______________________________________________ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss