SlideShare uma empresa Scribd logo
1 de 26
Baixar para ler offline
R a y B e l l i s
@ r a y b e l l i s
j Q u e r y U K – 2 0 1 3 / 0 4 / 1 9
1
Persistent Memoization using
HTML5 indexedDB and Promises
What is Memoization?
2
“Automatic caching of a pure function’s return value,
so that a subsequent call with the same parameter(s)
obtains the return value from a cache instead of
recalculating it.”
Avoiding:
  Expensive calculations
  Repeated AJAX calls…
Memoization Example Implementation
3
$.memoize = function(factory, ctx) {	
var cache = {};	
return function(key) {	
if (!(key in cache)) {	
cache[key] = factory.call(ctx, key);	
}	
return cache[key];	
};	
};
Usage #1 – Expensive Calculations
4
// recursive Fibonacci – O(~1.6^n) !!	
var fib = function(n) {	
return (n < 2) ? n : fib(n – 1) + fib(n – 2);	
}	
// wrap it	
fib = $.memoize(fib);	
The results of recursive calls are delivered from the cache
instead of being recalculated.
The algorithm improves from O(~1.6^n) to O(n) for first
run, and O(1) for previously calculated values of “n”.
Usage #2 – Repeated AJAX Calls
5
// AJAX function – returns a “Promise”	
// expensive to call – may even cost real money!	
function getGeo(ip) {	
return $.getJSON(url, {ip: ip});	
}	
// create a wrapped version	
var memoGeo = $.memoize(getGeo);	
memoGeo(“192.168.1.1”).done(function(data) {	
...	
});	
Repeated calls to the wrapped function for the same
input return the same promise, and thus the same result.
Usage #2 – Repeated AJAX Calls
6
// AJAX function – returns a “Promise”	
// expensive to call – may even cost real money!	
function getGeo(ip) {	
return $.getJSON(url, {ip: ip});	
}	
// create a wrapped version	
var memoGeo = $.memoize(getGeo);	
memoGeo(“192.168.1.1”).done(function(data) {	
...	
});	
Repeated calls to the wrapped function for the same
input return the same promise, and thus the same result.
How could I cache results between sessions?
HTML5 “indexedDB” to the Rescue
7
  Key/Value Store
  Values may be Objects
  localStorage only allows Strings
  Databases are origin specific (CORS)
  Multiple tables (“object stores”) per Database
  Asynchronous API
  Sync API exists but may be deprecated by W3C
  Schema changes require “Database Versioning”
Database Versioning
8
$.indexedDB = function(dbname, store) {	
var version; // initially undefined	
(function retry() {	
var request;	
if (typeof version === "undefined") {	
request = indexedDB.open(dbname); // open latest version	
} else {	
request = indexedDB.open(dbname, version) // or open specific version number	
}	
request.onsuccess = function(ev) {	
var db = ev.target.result;	
if (!db.objectStoreNames.contains(store)) { // if the store is missing	
version = db.version + 1; // increment version number	
db.close(); // close the DB	
retry(); // and open it again – NB: recursion!	
} else {	
// use the database here	
...	
}	
};	
request.onupgradeneeded = function(ev) {	
var db = ev.target.result;	
db.createObjectStore(store); // create new table	
};	
})(); // invoke immediately	
}
Callbacks…
9
$.indexedDB = function(dbname, store, callback) {	
var version; // initially undefined	
(function retry() {	
var request;	
if (typeof version === "undefined") {	
request = indexedDB.open(dbname); // open latest version	
} else {	
request = indexedDB.open(dbname, version) // or open specific version number	
}	
request.onsuccess = function(ev) {	
var db = ev.target.result;	
if (!db.objectStoreNames.contains(store)) { // if the store is missing	
version = db.version + 1; // increment version number	
db.close(); // close the DB	
retry(); // and open it again – NB: recursion!	
} else {	
// use the database here	
callback(db);	
}	
};	
request.onupgradeneeded = function(ev) {	
var db = ev.target.result;	
db.createObjectStore(store); // create new table	
};	
})(); // invoke immediately	
}
… are so 2010!
10
  jQuery Promises
  Introduced in jQuery 1.5
  Incredibly useful for asynchronous event handling
  Rich API
  $.when()
  .done()
  .then()
  etc
Let’s ditch those callbacks!
11
$.indexedDB = function(dbname, store) {	
var def = $.Deferred(); // I promise to return ...	
var version;	
(function retry() {	
var request;	
if (typeof version === "undefined") {	
request = indexedDB.open(dbname);	
} else {	
request = indexedDB.open(dbname, version);	
}	
request.onsuccess = function(ev) {	
var db = ev.target.result;	
if (!db.objectStoreNames.contains(store)) {	
version = db.version + 1;	
db.close();	
retry();	
} else {	
// use the database here	
def.resolve(db); // Tell the caller she can use the DB now	
} 	
};	
request.onupgradeneeded = function(ev) {	
var db = ev.target.result;	
db.createObjectStore(store);	
};	
})();	
return def.promise(); // I really do promise...	
};
Usage
12
$.indexedDB("indexed", store).done(function(db) {	
// use "db" here	
...	
});
Getting Back to Memoization
13
  One Database – avoids naming collisions
  One object store per memoized function
  Use Promises for consistency with other jQuery
async operations
No, I didn’t figure all this out in advance!
Code Walkthrough
14
$.memoizeForever = function(factory, store, keyPath, ctx) {	
var idb = $.indexedDB("indexed", store, keyPath);	
return function(key) {	
var def = $.Deferred();	
idb.done(function(db) {	
db.transaction(store).objectStore(store).get(key).onsuccess = function(ev) {	
if (typeof ev.target.result === "undefined") {	
$.when(factory.call(ctx, key)).done(function(data) {	
db.transaction(store, "readwrite").objectStore(store)	
.add(data).onsuccess = function()	
{	
def.resolve(data);	
};	
}).fail(def.reject);	
} else {	
def.resolve(ev.target.result);	
}	
};	
});	
return def.promise();	
};	
};
We need to return a function…
15
$.memoizeForever = function(factory, store, keyPath, ctx) {	
var idb = $.indexedDB("indexed", store, keyPath);	
return function(key) {	
var def = $.Deferred();	
idb.done(function(db) {	
db.transaction(store).objectStore(store).get(key).onsuccess = function(ev) {	
if (typeof ev.target.result === "undefined") {	
$.when(factory.call(ctx, key)).done(function(data) {	
db.transaction(store, "readwrite").objectStore(store)	
.add(data).onsuccess = function()	
{	
def.resolve(data);	
};	
}).fail(def.reject);	
} else {	
def.resolve(ev.target.result);	
}	
};	
});	
return def.promise();	
};	
};
that returns a Promise…
16
$.memoizeForever = function(factory, store, keyPath, ctx) {	
var idb = $.indexedDB("indexed", store, keyPath);	
return function(key) {	
var def = $.Deferred();	
idb.done(function(db) {	
db.transaction(store).objectStore(store).get(key).onsuccess = function(ev) {	
if (typeof ev.target.result === "undefined") {	
$.when(factory.call(ctx, key)).done(function(data) {	
db.transaction(store, "readwrite").objectStore(store)	
.add(data).onsuccess = function()	
{	
def.resolve(data);	
};	
}).fail(def.reject);	
} else {	
def.resolve(ev.target.result);	
}	
};	
});	
return def.promise();	
};	
};
and requires a DB connection…
17
$.memoizeForever = function(factory, store, keyPath, ctx) {	
var idb = $.indexedDB("indexed", store, keyPath);	
return function(key) {	
var def = $.Deferred();	
idb.done(function(db) {	
db.transaction(store).objectStore(store).get(key).onsuccess = function(ev) {	
if (typeof ev.target.result === "undefined") {	
$.when(factory.call(ctx, key)).done(function(data) {	
db.transaction(store, "readwrite").objectStore(store)	
.add(data).onsuccess = function()	
{	
def.resolve(data);	
};	
}).fail(def.reject);	
} else {	
def.resolve(ev.target.result);	
}	
};	
});	
return def.promise();	
};	
};
that looks up the key…
18
$.memoizeForever = function(factory, store, keyPath, ctx) {	
var idb = $.indexedDB("indexed", store, keyPath);	
return function(key) {	
var def = $.Deferred();	
idb.done(function(db) {	
db.transaction(store).objectStore(store).get(key).onsuccess = function(ev) {	
if (typeof ev.target.result === "undefined") {	
$.when(factory.call(ctx, key)).done(function(data) {	
db.transaction(store, "readwrite").objectStore(store)	
.add(data).onsuccess = function()	
{	
def.resolve(data);	
};	
}).fail(def.reject);	
} else {	
def.resolve(ev.target.result);	
}	
};	
});	
return def.promise();	
};	
};
and if found, resolves the Promise…
19
$.memoizeForever = function(factory, store, keyPath, ctx) {	
var idb = $.indexedDB("indexed", store, keyPath);	
return function(key) {	
var def = $.Deferred();	
idb.done(function(db) {	
db.transaction(store).objectStore(store).get(key).onsuccess = function(ev) {	
if (typeof ev.target.result === "undefined") {	
$.when(factory.call(ctx, key)).done(function(data) {	
db.transaction(store, "readwrite").objectStore(store)	
.add(data).onsuccess = function()	
{	
def.resolve(data);	
};	
}).fail(def.reject);	
} else {	
def.resolve(ev.target.result);	
}	
};	
});	
return def.promise();	
};	
};
otherwise, calls the original function…
20
$.memoizeForever = function(factory, store, keyPath, ctx) {	
var idb = $.indexedDB("indexed", store, keyPath);	
return function(key) {	
var def = $.Deferred();	
idb.done(function(db) {	
db.transaction(store).objectStore(store).get(key).onsuccess = function(ev) {	
if (typeof ev.target.result === "undefined") {	
$.when(factory.call(ctx, key)).done(function(data) {	
db.transaction(store, "readwrite").objectStore(store)	
.add(data).onsuccess = function()	
{	
def.resolve(data);	
};	
}).fail(def.reject);	
} else {	
def.resolve(ev.target.result);	
}	
};	
});	
return def.promise();	
};	
};
and $.when .done, stores it in the DB…
21
$.memoizeForever = function(factory, store, keyPath, ctx) {	
var idb = $.indexedDB("indexed", store, keyPath);	
return function(key) {	
var def = $.Deferred();	
idb.done(function(db) {	
db.transaction(store).objectStore(store).get(key).onsuccess = function(ev) {	
if (typeof ev.target.result === "undefined") {	
$.when(factory.call(ctx, key)).done(function(data) {	
db.transaction(store, "readwrite").objectStore(store)	
.add(data).onsuccess = function()	
{	
def.resolve(data);	
};	
}).fail(def.reject);	
} else {	
def.resolve(ev.target.result);	
}	
};	
});	
return def.promise();	
};	
};
and asynchronously resolves the Promise
22
$.memoizeForever = function(factory, store, keyPath, ctx) {	
var idb = $.indexedDB("indexed", store, keyPath);	
return function(key) {	
var def = $.Deferred();	
idb.done(function(db) {	
db.transaction(store).objectStore(store).get(key).onsuccess = function(ev) {	
if (typeof ev.target.result === "undefined") {	
$.when(factory.call(ctx, key)).done(function(data) {	
db.transaction(store, "readwrite").objectStore(store)	
.add(data).onsuccess = function()	
{	
def.resolve(data);	
};	
}).fail(def.reject);	
} else {	
def.resolve(ev.target.result);	
}	
};	
});	
return def.promise();	
};	
};
if it can…
23
$.memoizeForever = function(factory, store, keyPath, ctx) {	
var idb = $.indexedDB("indexed", store, keyPath);	
return function(key) {	
var def = $.Deferred();	
idb.done(function(db) {	
db.transaction(store).objectStore(store).get(key).onsuccess = function(ev) {	
if (typeof ev.target.result === "undefined") {	
$.when(factory.call(ctx, key)).done(function(data) {	
db.transaction(store, "readwrite").objectStore(store)	
.add(data).onsuccess = function()	
{	
def.resolve(data);	
};	
}).fail(def.reject);	
} else {	
def.resolve(ev.target.result);	
}	
};	
});	
return def.promise();	
};	
};
Persistent Memoization Usage
24
// AJAX function – returns a “Promise”	
// expensive to call – may even cost real money!	
function getGeo(ip) {	
return $.getJSON(url, {ip: ip});	
}	
// create a wrapped version	
// Object store name is "geoip" and JSON path to key is "ip"	
var memoGeo = $.memoizeForever(getGeo, "geoip", "ip");	
memoGeo("192.168.1.1”).done(function(data) {	
...	
});	
Now, repeated calls to the function return previously
obtained results, even between browser sessions!
Download
25
Source available at:
https://gist.github.com/raybellis/5254306#file-jquery-memoize-js
Questions?
26

Mais conteúdo relacionado

Mais procurados

Testing Backbone applications with Jasmine
Testing Backbone applications with JasmineTesting Backbone applications with Jasmine
Testing Backbone applications with JasmineLeon van der Grient
 
Forget about index.php and build you applications around HTTP!
Forget about index.php and build you applications around HTTP!Forget about index.php and build you applications around HTTP!
Forget about index.php and build you applications around HTTP!Kacper Gunia
 
Scaling Symfony2 apps with RabbitMQ - Symfony UK Meetup
Scaling Symfony2 apps with RabbitMQ - Symfony UK MeetupScaling Symfony2 apps with RabbitMQ - Symfony UK Meetup
Scaling Symfony2 apps with RabbitMQ - Symfony UK MeetupKacper Gunia
 
Http4s, Doobie and Circe: The Functional Web Stack
Http4s, Doobie and Circe: The Functional Web StackHttp4s, Doobie and Circe: The Functional Web Stack
Http4s, Doobie and Circe: The Functional Web StackGaryCoady
 
Adding Dependency Injection to Legacy Applications
Adding Dependency Injection to Legacy ApplicationsAdding Dependency Injection to Legacy Applications
Adding Dependency Injection to Legacy ApplicationsSam Hennessy
 
A Functional Guide to Cat Herding with PHP Generators
A Functional Guide to Cat Herding with PHP GeneratorsA Functional Guide to Cat Herding with PHP Generators
A Functional Guide to Cat Herding with PHP GeneratorsMark Baker
 
ES6 PPT FOR 2016
ES6 PPT FOR 2016ES6 PPT FOR 2016
ES6 PPT FOR 2016Manoj Kumar
 
Dutch PHP Conference - PHPSpec 2 - The only Design Tool you need
Dutch PHP Conference - PHPSpec 2 - The only Design Tool you needDutch PHP Conference - PHPSpec 2 - The only Design Tool you need
Dutch PHP Conference - PHPSpec 2 - The only Design Tool you needKacper Gunia
 
Rich domain model with symfony 2.5 and doctrine 2.5
Rich domain model with symfony 2.5 and doctrine 2.5Rich domain model with symfony 2.5 and doctrine 2.5
Rich domain model with symfony 2.5 and doctrine 2.5Leonardo Proietti
 
ZFConf 2010: Zend Framework & MVC, Model Implementation (Part 2, Dependency I...
ZFConf 2010: Zend Framework & MVC, Model Implementation (Part 2, Dependency I...ZFConf 2010: Zend Framework & MVC, Model Implementation (Part 2, Dependency I...
ZFConf 2010: Zend Framework & MVC, Model Implementation (Part 2, Dependency I...ZFConf Conference
 
Introduction to Nodejs
Introduction to NodejsIntroduction to Nodejs
Introduction to NodejsGabriele Lana
 
PHPSpec - the only Design Tool you need - 4Developers
PHPSpec - the only Design Tool you need - 4DevelopersPHPSpec - the only Design Tool you need - 4Developers
PHPSpec - the only Design Tool you need - 4DevelopersKacper Gunia
 
Your code sucks, let's fix it - DPC UnCon
Your code sucks, let's fix it - DPC UnConYour code sucks, let's fix it - DPC UnCon
Your code sucks, let's fix it - DPC UnConRafael Dohms
 
Introduction to Zend Framework web services
Introduction to Zend Framework web servicesIntroduction to Zend Framework web services
Introduction to Zend Framework web servicesMichelangelo van Dam
 
AST - the only true tool for building JavaScript
AST - the only true tool for building JavaScriptAST - the only true tool for building JavaScript
AST - the only true tool for building JavaScriptIngvar Stepanyan
 
G*ワークショップ in 仙台 Grails(とことん)入門
G*ワークショップ in 仙台 Grails(とことん)入門G*ワークショップ in 仙台 Grails(とことん)入門
G*ワークショップ in 仙台 Grails(とことん)入門Tsuyoshi Yamamoto
 
PHP Data Objects
PHP Data ObjectsPHP Data Objects
PHP Data ObjectsWez Furlong
 
Symfony without the framework
Symfony without the frameworkSymfony without the framework
Symfony without the frameworkGOG.com dev team
 

Mais procurados (20)

Testing Backbone applications with Jasmine
Testing Backbone applications with JasmineTesting Backbone applications with Jasmine
Testing Backbone applications with Jasmine
 
Forget about index.php and build you applications around HTTP!
Forget about index.php and build you applications around HTTP!Forget about index.php and build you applications around HTTP!
Forget about index.php and build you applications around HTTP!
 
Scaling Symfony2 apps with RabbitMQ - Symfony UK Meetup
Scaling Symfony2 apps with RabbitMQ - Symfony UK MeetupScaling Symfony2 apps with RabbitMQ - Symfony UK Meetup
Scaling Symfony2 apps with RabbitMQ - Symfony UK Meetup
 
Http4s, Doobie and Circe: The Functional Web Stack
Http4s, Doobie and Circe: The Functional Web StackHttp4s, Doobie and Circe: The Functional Web Stack
Http4s, Doobie and Circe: The Functional Web Stack
 
Adding Dependency Injection to Legacy Applications
Adding Dependency Injection to Legacy ApplicationsAdding Dependency Injection to Legacy Applications
Adding Dependency Injection to Legacy Applications
 
Drupal 8 migrate!
Drupal 8 migrate!Drupal 8 migrate!
Drupal 8 migrate!
 
A Functional Guide to Cat Herding with PHP Generators
A Functional Guide to Cat Herding with PHP GeneratorsA Functional Guide to Cat Herding with PHP Generators
A Functional Guide to Cat Herding with PHP Generators
 
ES6 PPT FOR 2016
ES6 PPT FOR 2016ES6 PPT FOR 2016
ES6 PPT FOR 2016
 
Dutch PHP Conference - PHPSpec 2 - The only Design Tool you need
Dutch PHP Conference - PHPSpec 2 - The only Design Tool you needDutch PHP Conference - PHPSpec 2 - The only Design Tool you need
Dutch PHP Conference - PHPSpec 2 - The only Design Tool you need
 
Rich domain model with symfony 2.5 and doctrine 2.5
Rich domain model with symfony 2.5 and doctrine 2.5Rich domain model with symfony 2.5 and doctrine 2.5
Rich domain model with symfony 2.5 and doctrine 2.5
 
ZFConf 2010: Zend Framework & MVC, Model Implementation (Part 2, Dependency I...
ZFConf 2010: Zend Framework & MVC, Model Implementation (Part 2, Dependency I...ZFConf 2010: Zend Framework & MVC, Model Implementation (Part 2, Dependency I...
ZFConf 2010: Zend Framework & MVC, Model Implementation (Part 2, Dependency I...
 
Introduction to Nodejs
Introduction to NodejsIntroduction to Nodejs
Introduction to Nodejs
 
PHPSpec - the only Design Tool you need - 4Developers
PHPSpec - the only Design Tool you need - 4DevelopersPHPSpec - the only Design Tool you need - 4Developers
PHPSpec - the only Design Tool you need - 4Developers
 
Your code sucks, let's fix it - DPC UnCon
Your code sucks, let's fix it - DPC UnConYour code sucks, let's fix it - DPC UnCon
Your code sucks, let's fix it - DPC UnCon
 
Introduction to Zend Framework web services
Introduction to Zend Framework web servicesIntroduction to Zend Framework web services
Introduction to Zend Framework web services
 
Drupal Render API
Drupal Render APIDrupal Render API
Drupal Render API
 
AST - the only true tool for building JavaScript
AST - the only true tool for building JavaScriptAST - the only true tool for building JavaScript
AST - the only true tool for building JavaScript
 
G*ワークショップ in 仙台 Grails(とことん)入門
G*ワークショップ in 仙台 Grails(とことん)入門G*ワークショップ in 仙台 Grails(とことん)入門
G*ワークショップ in 仙台 Grails(とことん)入門
 
PHP Data Objects
PHP Data ObjectsPHP Data Objects
PHP Data Objects
 
Symfony without the framework
Symfony without the frameworkSymfony without the framework
Symfony without the framework
 

Destaque

Booklet a4 (1)
Booklet a4 (1)Booklet a4 (1)
Booklet a4 (1)Edy Wijaya
 
Idj 3 (portfolio)
Idj 3 (portfolio)Idj 3 (portfolio)
Idj 3 (portfolio)Edy Wijaya
 
Características generales de los enfoques cualitativos
Características generales de los enfoques cualitativosCaracterísticas generales de los enfoques cualitativos
Características generales de los enfoques cualitativosNombre Apellidos
 
Productividad y pertinencia en la investigacion universitaria
Productividad y pertinencia en la investigacion universitariaProductividad y pertinencia en la investigacion universitaria
Productividad y pertinencia en la investigacion universitariaNombre Apellidos
 
2012年7月本田美国 (1)
2012年7月本田美国 (1)2012年7月本田美国 (1)
2012年7月本田美国 (1)Lassen Tours
 
Booklet a4 (2)
Booklet a4 (2)Booklet a4 (2)
Booklet a4 (2)Edy Wijaya
 
What to Know Before Visiting the Grand Canyon
What to Know Before Visiting the Grand CanyonWhat to Know Before Visiting the Grand Canyon
What to Know Before Visiting the Grand CanyonLassen Tours
 

Destaque (8)

Booklet a4 (1)
Booklet a4 (1)Booklet a4 (1)
Booklet a4 (1)
 
Idj 3 (portfolio)
Idj 3 (portfolio)Idj 3 (portfolio)
Idj 3 (portfolio)
 
Idj 4 slide
Idj 4 slideIdj 4 slide
Idj 4 slide
 
Características generales de los enfoques cualitativos
Características generales de los enfoques cualitativosCaracterísticas generales de los enfoques cualitativos
Características generales de los enfoques cualitativos
 
Productividad y pertinencia en la investigacion universitaria
Productividad y pertinencia en la investigacion universitariaProductividad y pertinencia en la investigacion universitaria
Productividad y pertinencia en la investigacion universitaria
 
2012年7月本田美国 (1)
2012年7月本田美国 (1)2012年7月本田美国 (1)
2012年7月本田美国 (1)
 
Booklet a4 (2)
Booklet a4 (2)Booklet a4 (2)
Booklet a4 (2)
 
What to Know Before Visiting the Grand Canyon
What to Know Before Visiting the Grand CanyonWhat to Know Before Visiting the Grand Canyon
What to Know Before Visiting the Grand Canyon
 

Semelhante a Persistent Memoization with HTML5 indexedDB and jQuery Promises

IndexedDB - Querying and Performance
IndexedDB - Querying and PerformanceIndexedDB - Querying and Performance
IndexedDB - Querying and PerformanceParashuram N
 
Intro to IndexedDB (Beta)
Intro to IndexedDB (Beta)Intro to IndexedDB (Beta)
Intro to IndexedDB (Beta)Mike West
 
HelsinkiJS meet-up. Dmitry Soshnikov - ECMAScript 6
HelsinkiJS meet-up. Dmitry Soshnikov - ECMAScript 6HelsinkiJS meet-up. Dmitry Soshnikov - ECMAScript 6
HelsinkiJS meet-up. Dmitry Soshnikov - ECMAScript 6Dmitry Soshnikov
 
Development Approach
Development ApproachDevelopment Approach
Development Approachalexkingorg
 
The Beauty Of Java Script V5a
The Beauty Of Java Script V5aThe Beauty Of Java Script V5a
The Beauty Of Java Script V5arajivmordani
 
jQuery: out with the old, in with the new
jQuery: out with the old, in with the newjQuery: out with the old, in with the new
jQuery: out with the old, in with the newRemy Sharp
 
Node.js for PHP developers
Node.js for PHP developersNode.js for PHP developers
Node.js for PHP developersAndrew Eddie
 
Matthew Eernisse, NodeJs, .toster {webdev}
Matthew Eernisse, NodeJs, .toster {webdev}Matthew Eernisse, NodeJs, .toster {webdev}
Matthew Eernisse, NodeJs, .toster {webdev}.toster
 
JavaScript for PHP developers
JavaScript for PHP developersJavaScript for PHP developers
JavaScript for PHP developersStoyan Stefanov
 
Chaining and function composition with lodash / underscore
Chaining and function composition with lodash / underscoreChaining and function composition with lodash / underscore
Chaining and function composition with lodash / underscoreNicolas Carlo
 
Mongoskin - Guilin
Mongoskin - GuilinMongoskin - Guilin
Mongoskin - GuilinJackson Tian
 
Symfony2, creare bundle e valore per il cliente
Symfony2, creare bundle e valore per il clienteSymfony2, creare bundle e valore per il cliente
Symfony2, creare bundle e valore per il clienteLeonardo Proietti
 
Think Async: Asynchronous Patterns in NodeJS
Think Async: Asynchronous Patterns in NodeJSThink Async: Asynchronous Patterns in NodeJS
Think Async: Asynchronous Patterns in NodeJSAdam L Barrett
 
Future of Web Apps: Google Gears
Future of Web Apps: Google GearsFuture of Web Apps: Google Gears
Future of Web Apps: Google Gearsdion
 
Javascript: the important bits
Javascript: the important bitsJavascript: the important bits
Javascript: the important bitsChris Saylor
 

Semelhante a Persistent Memoization with HTML5 indexedDB and jQuery Promises (20)

IndexedDB - Querying and Performance
IndexedDB - Querying and PerformanceIndexedDB - Querying and Performance
IndexedDB - Querying and Performance
 
New in php 7
New in php 7New in php 7
New in php 7
 
Intro to IndexedDB (Beta)
Intro to IndexedDB (Beta)Intro to IndexedDB (Beta)
Intro to IndexedDB (Beta)
 
The Beauty of Java Script
The Beauty of Java ScriptThe Beauty of Java Script
The Beauty of Java Script
 
Java script for web developer
Java script for web developerJava script for web developer
Java script for web developer
 
HelsinkiJS meet-up. Dmitry Soshnikov - ECMAScript 6
HelsinkiJS meet-up. Dmitry Soshnikov - ECMAScript 6HelsinkiJS meet-up. Dmitry Soshnikov - ECMAScript 6
HelsinkiJS meet-up. Dmitry Soshnikov - ECMAScript 6
 
Development Approach
Development ApproachDevelopment Approach
Development Approach
 
The Beauty Of Java Script V5a
The Beauty Of Java Script V5aThe Beauty Of Java Script V5a
The Beauty Of Java Script V5a
 
jQuery: out with the old, in with the new
jQuery: out with the old, in with the newjQuery: out with the old, in with the new
jQuery: out with the old, in with the new
 
Node.js for PHP developers
Node.js for PHP developersNode.js for PHP developers
Node.js for PHP developers
 
Matthew Eernisse, NodeJs, .toster {webdev}
Matthew Eernisse, NodeJs, .toster {webdev}Matthew Eernisse, NodeJs, .toster {webdev}
Matthew Eernisse, NodeJs, .toster {webdev}
 
JavaScript for PHP developers
JavaScript for PHP developersJavaScript for PHP developers
JavaScript for PHP developers
 
Indexed db
Indexed dbIndexed db
Indexed db
 
Chaining and function composition with lodash / underscore
Chaining and function composition with lodash / underscoreChaining and function composition with lodash / underscore
Chaining and function composition with lodash / underscore
 
Mongoskin - Guilin
Mongoskin - GuilinMongoskin - Guilin
Mongoskin - Guilin
 
Symfony2, creare bundle e valore per il cliente
Symfony2, creare bundle e valore per il clienteSymfony2, creare bundle e valore per il cliente
Symfony2, creare bundle e valore per il cliente
 
Think Async: Asynchronous Patterns in NodeJS
Think Async: Asynchronous Patterns in NodeJSThink Async: Asynchronous Patterns in NodeJS
Think Async: Asynchronous Patterns in NodeJS
 
Future of Web Apps: Google Gears
Future of Web Apps: Google GearsFuture of Web Apps: Google Gears
Future of Web Apps: Google Gears
 
Javascript: the important bits
Javascript: the important bitsJavascript: the important bits
Javascript: the important bits
 
JavaScript JQUERY AJAX
JavaScript JQUERY AJAXJavaScript JQUERY AJAX
JavaScript JQUERY AJAX
 

Último

Moving Beyond Passwords: FIDO Paris Seminar.pdf
Moving Beyond Passwords: FIDO Paris Seminar.pdfMoving Beyond Passwords: FIDO Paris Seminar.pdf
Moving Beyond Passwords: FIDO Paris Seminar.pdfLoriGlavin3
 
Digital Identity is Under Attack: FIDO Paris Seminar.pptx
Digital Identity is Under Attack: FIDO Paris Seminar.pptxDigital Identity is Under Attack: FIDO Paris Seminar.pptx
Digital Identity is Under Attack: FIDO Paris Seminar.pptxLoriGlavin3
 
The Fit for Passkeys for Employee and Consumer Sign-ins: FIDO Paris Seminar.pptx
The Fit for Passkeys for Employee and Consumer Sign-ins: FIDO Paris Seminar.pptxThe Fit for Passkeys for Employee and Consumer Sign-ins: FIDO Paris Seminar.pptx
The Fit for Passkeys for Employee and Consumer Sign-ins: FIDO Paris Seminar.pptxLoriGlavin3
 
Gen AI in Business - Global Trends Report 2024.pdf
Gen AI in Business - Global Trends Report 2024.pdfGen AI in Business - Global Trends Report 2024.pdf
Gen AI in Business - Global Trends Report 2024.pdfAddepto
 
Unraveling Multimodality with Large Language Models.pdf
Unraveling Multimodality with Large Language Models.pdfUnraveling Multimodality with Large Language Models.pdf
Unraveling Multimodality with Large Language Models.pdfAlex Barbosa Coqueiro
 
Take control of your SAP testing with UiPath Test Suite
Take control of your SAP testing with UiPath Test SuiteTake control of your SAP testing with UiPath Test Suite
Take control of your SAP testing with UiPath Test SuiteDianaGray10
 
Nell’iperspazio con Rocket: il Framework Web di Rust!
Nell’iperspazio con Rocket: il Framework Web di Rust!Nell’iperspazio con Rocket: il Framework Web di Rust!
Nell’iperspazio con Rocket: il Framework Web di Rust!Commit University
 
"Debugging python applications inside k8s environment", Andrii Soldatenko
"Debugging python applications inside k8s environment", Andrii Soldatenko"Debugging python applications inside k8s environment", Andrii Soldatenko
"Debugging python applications inside k8s environment", Andrii SoldatenkoFwdays
 
What is Artificial Intelligence?????????
What is Artificial Intelligence?????????What is Artificial Intelligence?????????
What is Artificial Intelligence?????????blackmambaettijean
 
Artificial intelligence in cctv survelliance.pptx
Artificial intelligence in cctv survelliance.pptxArtificial intelligence in cctv survelliance.pptx
Artificial intelligence in cctv survelliance.pptxhariprasad279825
 
Anypoint Exchange: It’s Not Just a Repo!
Anypoint Exchange: It’s Not Just a Repo!Anypoint Exchange: It’s Not Just a Repo!
Anypoint Exchange: It’s Not Just a Repo!Manik S Magar
 
What is DBT - The Ultimate Data Build Tool.pdf
What is DBT - The Ultimate Data Build Tool.pdfWhat is DBT - The Ultimate Data Build Tool.pdf
What is DBT - The Ultimate Data Build Tool.pdfMounikaPolabathina
 
Developer Data Modeling Mistakes: From Postgres to NoSQL
Developer Data Modeling Mistakes: From Postgres to NoSQLDeveloper Data Modeling Mistakes: From Postgres to NoSQL
Developer Data Modeling Mistakes: From Postgres to NoSQLScyllaDB
 
How AI, OpenAI, and ChatGPT impact business and software.
How AI, OpenAI, and ChatGPT impact business and software.How AI, OpenAI, and ChatGPT impact business and software.
How AI, OpenAI, and ChatGPT impact business and software.Curtis Poe
 
What's New in Teams Calling, Meetings and Devices March 2024
What's New in Teams Calling, Meetings and Devices March 2024What's New in Teams Calling, Meetings and Devices March 2024
What's New in Teams Calling, Meetings and Devices March 2024Stephanie Beckett
 
WordPress Websites for Engineers: Elevate Your Brand
WordPress Websites for Engineers: Elevate Your BrandWordPress Websites for Engineers: Elevate Your Brand
WordPress Websites for Engineers: Elevate Your Brandgvaughan
 
From Family Reminiscence to Scholarly Archive .
From Family Reminiscence to Scholarly Archive .From Family Reminiscence to Scholarly Archive .
From Family Reminiscence to Scholarly Archive .Alan Dix
 
Transcript: New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024
Transcript: New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024Transcript: New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024
Transcript: New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024BookNet Canada
 
New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024
New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024
New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024BookNet Canada
 
Passkey Providers and Enabling Portability: FIDO Paris Seminar.pptx
Passkey Providers and Enabling Portability: FIDO Paris Seminar.pptxPasskey Providers and Enabling Portability: FIDO Paris Seminar.pptx
Passkey Providers and Enabling Portability: FIDO Paris Seminar.pptxLoriGlavin3
 

Último (20)

Moving Beyond Passwords: FIDO Paris Seminar.pdf
Moving Beyond Passwords: FIDO Paris Seminar.pdfMoving Beyond Passwords: FIDO Paris Seminar.pdf
Moving Beyond Passwords: FIDO Paris Seminar.pdf
 
Digital Identity is Under Attack: FIDO Paris Seminar.pptx
Digital Identity is Under Attack: FIDO Paris Seminar.pptxDigital Identity is Under Attack: FIDO Paris Seminar.pptx
Digital Identity is Under Attack: FIDO Paris Seminar.pptx
 
The Fit for Passkeys for Employee and Consumer Sign-ins: FIDO Paris Seminar.pptx
The Fit for Passkeys for Employee and Consumer Sign-ins: FIDO Paris Seminar.pptxThe Fit for Passkeys for Employee and Consumer Sign-ins: FIDO Paris Seminar.pptx
The Fit for Passkeys for Employee and Consumer Sign-ins: FIDO Paris Seminar.pptx
 
Gen AI in Business - Global Trends Report 2024.pdf
Gen AI in Business - Global Trends Report 2024.pdfGen AI in Business - Global Trends Report 2024.pdf
Gen AI in Business - Global Trends Report 2024.pdf
 
Unraveling Multimodality with Large Language Models.pdf
Unraveling Multimodality with Large Language Models.pdfUnraveling Multimodality with Large Language Models.pdf
Unraveling Multimodality with Large Language Models.pdf
 
Take control of your SAP testing with UiPath Test Suite
Take control of your SAP testing with UiPath Test SuiteTake control of your SAP testing with UiPath Test Suite
Take control of your SAP testing with UiPath Test Suite
 
Nell’iperspazio con Rocket: il Framework Web di Rust!
Nell’iperspazio con Rocket: il Framework Web di Rust!Nell’iperspazio con Rocket: il Framework Web di Rust!
Nell’iperspazio con Rocket: il Framework Web di Rust!
 
"Debugging python applications inside k8s environment", Andrii Soldatenko
"Debugging python applications inside k8s environment", Andrii Soldatenko"Debugging python applications inside k8s environment", Andrii Soldatenko
"Debugging python applications inside k8s environment", Andrii Soldatenko
 
What is Artificial Intelligence?????????
What is Artificial Intelligence?????????What is Artificial Intelligence?????????
What is Artificial Intelligence?????????
 
Artificial intelligence in cctv survelliance.pptx
Artificial intelligence in cctv survelliance.pptxArtificial intelligence in cctv survelliance.pptx
Artificial intelligence in cctv survelliance.pptx
 
Anypoint Exchange: It’s Not Just a Repo!
Anypoint Exchange: It’s Not Just a Repo!Anypoint Exchange: It’s Not Just a Repo!
Anypoint Exchange: It’s Not Just a Repo!
 
What is DBT - The Ultimate Data Build Tool.pdf
What is DBT - The Ultimate Data Build Tool.pdfWhat is DBT - The Ultimate Data Build Tool.pdf
What is DBT - The Ultimate Data Build Tool.pdf
 
Developer Data Modeling Mistakes: From Postgres to NoSQL
Developer Data Modeling Mistakes: From Postgres to NoSQLDeveloper Data Modeling Mistakes: From Postgres to NoSQL
Developer Data Modeling Mistakes: From Postgres to NoSQL
 
How AI, OpenAI, and ChatGPT impact business and software.
How AI, OpenAI, and ChatGPT impact business and software.How AI, OpenAI, and ChatGPT impact business and software.
How AI, OpenAI, and ChatGPT impact business and software.
 
What's New in Teams Calling, Meetings and Devices March 2024
What's New in Teams Calling, Meetings and Devices March 2024What's New in Teams Calling, Meetings and Devices March 2024
What's New in Teams Calling, Meetings and Devices March 2024
 
WordPress Websites for Engineers: Elevate Your Brand
WordPress Websites for Engineers: Elevate Your BrandWordPress Websites for Engineers: Elevate Your Brand
WordPress Websites for Engineers: Elevate Your Brand
 
From Family Reminiscence to Scholarly Archive .
From Family Reminiscence to Scholarly Archive .From Family Reminiscence to Scholarly Archive .
From Family Reminiscence to Scholarly Archive .
 
Transcript: New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024
Transcript: New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024Transcript: New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024
Transcript: New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024
 
New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024
New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024
New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024
 
Passkey Providers and Enabling Portability: FIDO Paris Seminar.pptx
Passkey Providers and Enabling Portability: FIDO Paris Seminar.pptxPasskey Providers and Enabling Portability: FIDO Paris Seminar.pptx
Passkey Providers and Enabling Portability: FIDO Paris Seminar.pptx
 

Persistent Memoization with HTML5 indexedDB and jQuery Promises

  • 1. R a y B e l l i s @ r a y b e l l i s j Q u e r y U K – 2 0 1 3 / 0 4 / 1 9 1 Persistent Memoization using HTML5 indexedDB and Promises
  • 2. What is Memoization? 2 “Automatic caching of a pure function’s return value, so that a subsequent call with the same parameter(s) obtains the return value from a cache instead of recalculating it.” Avoiding:   Expensive calculations   Repeated AJAX calls…
  • 3. Memoization Example Implementation 3 $.memoize = function(factory, ctx) { var cache = {}; return function(key) { if (!(key in cache)) { cache[key] = factory.call(ctx, key); } return cache[key]; }; };
  • 4. Usage #1 – Expensive Calculations 4 // recursive Fibonacci – O(~1.6^n) !! var fib = function(n) { return (n < 2) ? n : fib(n – 1) + fib(n – 2); } // wrap it fib = $.memoize(fib); The results of recursive calls are delivered from the cache instead of being recalculated. The algorithm improves from O(~1.6^n) to O(n) for first run, and O(1) for previously calculated values of “n”.
  • 5. Usage #2 – Repeated AJAX Calls 5 // AJAX function – returns a “Promise” // expensive to call – may even cost real money! function getGeo(ip) { return $.getJSON(url, {ip: ip}); } // create a wrapped version var memoGeo = $.memoize(getGeo); memoGeo(“192.168.1.1”).done(function(data) { ... }); Repeated calls to the wrapped function for the same input return the same promise, and thus the same result.
  • 6. Usage #2 – Repeated AJAX Calls 6 // AJAX function – returns a “Promise” // expensive to call – may even cost real money! function getGeo(ip) { return $.getJSON(url, {ip: ip}); } // create a wrapped version var memoGeo = $.memoize(getGeo); memoGeo(“192.168.1.1”).done(function(data) { ... }); Repeated calls to the wrapped function for the same input return the same promise, and thus the same result. How could I cache results between sessions?
  • 7. HTML5 “indexedDB” to the Rescue 7   Key/Value Store   Values may be Objects   localStorage only allows Strings   Databases are origin specific (CORS)   Multiple tables (“object stores”) per Database   Asynchronous API   Sync API exists but may be deprecated by W3C   Schema changes require “Database Versioning”
  • 8. Database Versioning 8 $.indexedDB = function(dbname, store) { var version; // initially undefined (function retry() { var request; if (typeof version === "undefined") { request = indexedDB.open(dbname); // open latest version } else { request = indexedDB.open(dbname, version) // or open specific version number } request.onsuccess = function(ev) { var db = ev.target.result; if (!db.objectStoreNames.contains(store)) { // if the store is missing version = db.version + 1; // increment version number db.close(); // close the DB retry(); // and open it again – NB: recursion! } else { // use the database here ... } }; request.onupgradeneeded = function(ev) { var db = ev.target.result; db.createObjectStore(store); // create new table }; })(); // invoke immediately }
  • 9. Callbacks… 9 $.indexedDB = function(dbname, store, callback) { var version; // initially undefined (function retry() { var request; if (typeof version === "undefined") { request = indexedDB.open(dbname); // open latest version } else { request = indexedDB.open(dbname, version) // or open specific version number } request.onsuccess = function(ev) { var db = ev.target.result; if (!db.objectStoreNames.contains(store)) { // if the store is missing version = db.version + 1; // increment version number db.close(); // close the DB retry(); // and open it again – NB: recursion! } else { // use the database here callback(db); } }; request.onupgradeneeded = function(ev) { var db = ev.target.result; db.createObjectStore(store); // create new table }; })(); // invoke immediately }
  • 10. … are so 2010! 10   jQuery Promises   Introduced in jQuery 1.5   Incredibly useful for asynchronous event handling   Rich API   $.when()   .done()   .then()   etc
  • 11. Let’s ditch those callbacks! 11 $.indexedDB = function(dbname, store) { var def = $.Deferred(); // I promise to return ... var version; (function retry() { var request; if (typeof version === "undefined") { request = indexedDB.open(dbname); } else { request = indexedDB.open(dbname, version); } request.onsuccess = function(ev) { var db = ev.target.result; if (!db.objectStoreNames.contains(store)) { version = db.version + 1; db.close(); retry(); } else { // use the database here def.resolve(db); // Tell the caller she can use the DB now } }; request.onupgradeneeded = function(ev) { var db = ev.target.result; db.createObjectStore(store); }; })(); return def.promise(); // I really do promise... };
  • 13. Getting Back to Memoization 13   One Database – avoids naming collisions   One object store per memoized function   Use Promises for consistency with other jQuery async operations No, I didn’t figure all this out in advance!
  • 14. Code Walkthrough 14 $.memoizeForever = function(factory, store, keyPath, ctx) { var idb = $.indexedDB("indexed", store, keyPath); return function(key) { var def = $.Deferred(); idb.done(function(db) { db.transaction(store).objectStore(store).get(key).onsuccess = function(ev) { if (typeof ev.target.result === "undefined") { $.when(factory.call(ctx, key)).done(function(data) { db.transaction(store, "readwrite").objectStore(store) .add(data).onsuccess = function() { def.resolve(data); }; }).fail(def.reject); } else { def.resolve(ev.target.result); } }; }); return def.promise(); }; };
  • 15. We need to return a function… 15 $.memoizeForever = function(factory, store, keyPath, ctx) { var idb = $.indexedDB("indexed", store, keyPath); return function(key) { var def = $.Deferred(); idb.done(function(db) { db.transaction(store).objectStore(store).get(key).onsuccess = function(ev) { if (typeof ev.target.result === "undefined") { $.when(factory.call(ctx, key)).done(function(data) { db.transaction(store, "readwrite").objectStore(store) .add(data).onsuccess = function() { def.resolve(data); }; }).fail(def.reject); } else { def.resolve(ev.target.result); } }; }); return def.promise(); }; };
  • 16. that returns a Promise… 16 $.memoizeForever = function(factory, store, keyPath, ctx) { var idb = $.indexedDB("indexed", store, keyPath); return function(key) { var def = $.Deferred(); idb.done(function(db) { db.transaction(store).objectStore(store).get(key).onsuccess = function(ev) { if (typeof ev.target.result === "undefined") { $.when(factory.call(ctx, key)).done(function(data) { db.transaction(store, "readwrite").objectStore(store) .add(data).onsuccess = function() { def.resolve(data); }; }).fail(def.reject); } else { def.resolve(ev.target.result); } }; }); return def.promise(); }; };
  • 17. and requires a DB connection… 17 $.memoizeForever = function(factory, store, keyPath, ctx) { var idb = $.indexedDB("indexed", store, keyPath); return function(key) { var def = $.Deferred(); idb.done(function(db) { db.transaction(store).objectStore(store).get(key).onsuccess = function(ev) { if (typeof ev.target.result === "undefined") { $.when(factory.call(ctx, key)).done(function(data) { db.transaction(store, "readwrite").objectStore(store) .add(data).onsuccess = function() { def.resolve(data); }; }).fail(def.reject); } else { def.resolve(ev.target.result); } }; }); return def.promise(); }; };
  • 18. that looks up the key… 18 $.memoizeForever = function(factory, store, keyPath, ctx) { var idb = $.indexedDB("indexed", store, keyPath); return function(key) { var def = $.Deferred(); idb.done(function(db) { db.transaction(store).objectStore(store).get(key).onsuccess = function(ev) { if (typeof ev.target.result === "undefined") { $.when(factory.call(ctx, key)).done(function(data) { db.transaction(store, "readwrite").objectStore(store) .add(data).onsuccess = function() { def.resolve(data); }; }).fail(def.reject); } else { def.resolve(ev.target.result); } }; }); return def.promise(); }; };
  • 19. and if found, resolves the Promise… 19 $.memoizeForever = function(factory, store, keyPath, ctx) { var idb = $.indexedDB("indexed", store, keyPath); return function(key) { var def = $.Deferred(); idb.done(function(db) { db.transaction(store).objectStore(store).get(key).onsuccess = function(ev) { if (typeof ev.target.result === "undefined") { $.when(factory.call(ctx, key)).done(function(data) { db.transaction(store, "readwrite").objectStore(store) .add(data).onsuccess = function() { def.resolve(data); }; }).fail(def.reject); } else { def.resolve(ev.target.result); } }; }); return def.promise(); }; };
  • 20. otherwise, calls the original function… 20 $.memoizeForever = function(factory, store, keyPath, ctx) { var idb = $.indexedDB("indexed", store, keyPath); return function(key) { var def = $.Deferred(); idb.done(function(db) { db.transaction(store).objectStore(store).get(key).onsuccess = function(ev) { if (typeof ev.target.result === "undefined") { $.when(factory.call(ctx, key)).done(function(data) { db.transaction(store, "readwrite").objectStore(store) .add(data).onsuccess = function() { def.resolve(data); }; }).fail(def.reject); } else { def.resolve(ev.target.result); } }; }); return def.promise(); }; };
  • 21. and $.when .done, stores it in the DB… 21 $.memoizeForever = function(factory, store, keyPath, ctx) { var idb = $.indexedDB("indexed", store, keyPath); return function(key) { var def = $.Deferred(); idb.done(function(db) { db.transaction(store).objectStore(store).get(key).onsuccess = function(ev) { if (typeof ev.target.result === "undefined") { $.when(factory.call(ctx, key)).done(function(data) { db.transaction(store, "readwrite").objectStore(store) .add(data).onsuccess = function() { def.resolve(data); }; }).fail(def.reject); } else { def.resolve(ev.target.result); } }; }); return def.promise(); }; };
  • 22. and asynchronously resolves the Promise 22 $.memoizeForever = function(factory, store, keyPath, ctx) { var idb = $.indexedDB("indexed", store, keyPath); return function(key) { var def = $.Deferred(); idb.done(function(db) { db.transaction(store).objectStore(store).get(key).onsuccess = function(ev) { if (typeof ev.target.result === "undefined") { $.when(factory.call(ctx, key)).done(function(data) { db.transaction(store, "readwrite").objectStore(store) .add(data).onsuccess = function() { def.resolve(data); }; }).fail(def.reject); } else { def.resolve(ev.target.result); } }; }); return def.promise(); }; };
  • 23. if it can… 23 $.memoizeForever = function(factory, store, keyPath, ctx) { var idb = $.indexedDB("indexed", store, keyPath); return function(key) { var def = $.Deferred(); idb.done(function(db) { db.transaction(store).objectStore(store).get(key).onsuccess = function(ev) { if (typeof ev.target.result === "undefined") { $.when(factory.call(ctx, key)).done(function(data) { db.transaction(store, "readwrite").objectStore(store) .add(data).onsuccess = function() { def.resolve(data); }; }).fail(def.reject); } else { def.resolve(ev.target.result); } }; }); return def.promise(); }; };
  • 24. Persistent Memoization Usage 24 // AJAX function – returns a “Promise” // expensive to call – may even cost real money! function getGeo(ip) { return $.getJSON(url, {ip: ip}); } // create a wrapped version // Object store name is "geoip" and JSON path to key is "ip" var memoGeo = $.memoizeForever(getGeo, "geoip", "ip"); memoGeo("192.168.1.1”).done(function(data) { ... }); Now, repeated calls to the function return previously obtained results, even between browser sessions!