Accessing indexedDB in ServiceWorker. Race condition - html

There aren't many examples demonstrating indexedDB in a ServiceWorker yet, but the ones I saw were all structured like this:
const request = indexedDB.open( 'myDB', 1 );
var db;
request.onupgradeneeded = ...
request.onsuccess = function() {
db = this.result; // Average 8ms
};
self.onfetch = function(e)
{
const requestURL = new URL( e.request.url ),
path = requestURL.pathname;
if( path === '/test' )
{
const response = new Promise( function( resolve )
{
console.log( performance.now(), typeof db ); // Average 15ms
db.transaction( 'cache' ).objectStore( 'cache' ).get( 'test' ).onsuccess = function()
{
resolve( new Response( this.result, { headers: { 'content-type':'text/plain' } } ) );
}
});
e.respondWith( response );
}
}
Is this likely to fail when the ServiceWorker starts up, and if so what is a robust way of accessing indexedDB in a ServiceWorker?

Opening the IDB every time the ServiceWorker starts up is unlikely to be optimal, you'll end up opening it even when it isn't used. Instead, open the db when you need it. A singleton is really useful here (see https://github.com/jakearchibald/svgomg/blob/master/src/js/utils/storage.js#L5), so you don't need to open IDB twice if it's used twice in its lifetime.
The "activate" event is a great place to open IDB and let any "onupdateneeded" events run, as the old version of ServiceWorker is out of the way.

You can wrap a transaction in a promise like so:
var tx = db.transaction(scope, mode);
var p = new Promise(function(resolve, reject) {
tx.onabort = function() { reject(tx.error); };
tx.oncomplete = function() { resolve(); };
});
Now p will resolve/reject when the transaction completes/aborts. So you can do arbitrary logic in the tx transaction, and p.then(...) and/or pass a dependent promise into e.respondWith() or e.waitUntil() etc.
As noted by other commenters, we really do need to promisify IndexedDB. But the composition of its post-task autocommit model and the microtask queues that Promises use make it... nontrivial to do so without basically completely replacing the API. But (as an implementer and one of the spec editors) I'm actively prototyping some ideas.

I don't know of anything special about accessing IndexedDB from the context of a service worker via accessing IndexedDB via a controlled page.
Promises obviously makes your life much easier within a service worker, so I've found using something like, e.g., https://gist.github.com/inexorabletash/c8069c042b734519680c to be useful instead of the raw IndexedDB API. But it's not mandatory as long as you create and manage your own promises to reflect the state of the asynchronous IndexedDB operations.
The main thing to keep in mind when writing a fetch event handler (and this isn't specific to using IndexedDB), is that if you call event.respondWith(), you need to pass in either a Response object or a promise that resolves with a Response object. As long as you're doing that, it shouldn't matter whether your Response is constructed from IndexedDB entries or the Cache API or elsewhere.
Are you running into any actual problems with the code you posted, or was this more of a theoretical question?

Related

How to get a block's statut for a transaction in Ethereum network

This question follows a previous one that I asked : Trust Querying Event in Ethereum.
In my back-end I listen for an event after a specific transaction. My back-end get the event just after the transaction is added to a new block. However to be sure that the block will not be removed we need to wait that its statut becomes 'finalized'.
My question is : how can I manage to do it using ethersjs ?
Thanks,
I am just poking around stackoverflow for the first time and noticed your question. Since you're building automation around event monitoring, I wanted to recommend a great, free tool that handles this issue around block finality before exposing the event data you need via REST API.
https://www.hal.xyz/products/hal-stream
Hopefully it is helpful :)
I didn't manage to find an ideal solution. However I tried something like this and it seems to work :
provider.on(filter,async (log) => {
const listener ={
async action(){
try{
let iface = new ethers.utils.Interface(abi)
let amount = iface.parseLog(log).args.tokens;
let blockHash = log.blockHash;
let transactionHash = log.transactionHash;
let removed = log.removed;
if(!removed){
this.executedPayment = await ExecutedPayment.create(
{
PaymentId:uuid,
Amount:ethers.utils.formatUnits(amount, DECIMAL_USDT),
TransactionHash:transactionHash,
BlockHash:blockHash,
StateId:1
}
)
clearTimeout(this.finalizationTimeout);
this.finalizationTimeout = setTimeout(async () => {
await this.executedPayment.update({StateId:2})
await this.executedPayment.save()
provider.off(filter)
}, 900000) //if no reorgs happened in the last 15 minutes, we consider the block as finalized
}else{
this.executedPayment.update({StateId:3})
clearTimeout(this.finalizationTimeout);
}
}catch(e){
console.log("An error occured : "+e)
}
}
}
listener.action()
})
If you have another solution I'll be glad to know.

Returning promise from reflux store

I'm working on my first react/reflux app so I may be approaching this problem in completely the wrong way. I'm trying to return a promise from a reflux store's action handler. This is the minimum code that represents how I'm trying to do this. If I display this in the browser, I get an error saying that the promise is never caught, because the result of the onLogin function is not passed back when the action is initiated. What is the best way to do this?
var Reflux = require('reflux');
var React = require('react/addons')
const Action = Reflux.createAction();
const Store = Reflux.createStore({
init: function() {
this.listenTo(Action, this.onAction);
},
onAction: function(username, password) {
var p = new Promise((resolve, reject) => {
reject('Bad password');
});
return p;
}
});
var LoginForm = React.createClass({
mixins: [Reflux.connect(Store, 'store')],
login: function() {
Action('nate', 'password1').catch(function(e) {
console.log(e); // This line is never executed
});
},
render: function() {
return (
<a onClick={this.login} href="#">login</a>
)
}
});
React.render(<LoginForm />, document.body);
Several things seem a bit confused here.
Reflux.connect(Store, 'store') is a shorthand for listening to the provided store, and automatically set the "store" property of your component state to whatever is passed in your store's this.trigger() call. However, your store never calls this.trigger so "store" in your component's state will never be updated. Returning a value from your store's action handlers doesn't trigger an update.
Stores should listen to actions to update their internal state, and typically then broadcast this state update by calling this.trigger. No component is going to get your returned promise from the store's onAction unless it explicitly calls Store.onAction (and then it doesn't matter if the actual action was invoked or not).
Async work should typically happen in the action's preEmit hook, not in the store. You should then also declare the action as async in createAction by setting the asyncResult option to true to automatically create "completed" and "failed" child actions. Check out the Reflux documentation here to learn about async events. Async actions automatically return promises, whose resolve and reject are called when the "completed" and "failed" sub-actions are called respectively. This is a bit opinionated, but that is definitely what I perceive is the intended Reflux way.

Only fetch objects with specified keys in Firebase from large index

I have an array = [ 'something', 'other' ]
And I want to retrieve only the values of those 2 ids from Firebase, which contains more than 2 items ( potentially millions ), but if I do this:
var questionRef = new Firebase(fireBaseURL+"/morethanamillionitems/");
loadUID.once('value', function (dataSnapshot) {
dataSnapshot.forEach(function(childSnapshot) { // Firebase method
console.log(dataSnapshot.numChildren()); // potentially outputs 1.000.000 +
var uid = childSnapshot.name();
var childData = childSnapshot.val();
console.log(uid.indexOf('something'));
result.push(uid)
});
}
I first basically load the whole database, which is not that efficient
Now I could do:
array.forEach(key, function() {
var questionRef = new Firebase(fireBaseURL+"/morethanamillionitems/"+key);
refID = questionRef.val();
result.push(refID);
})
Or maybe:
questionRef = new Firebase(fireBaseURL+"/morethanamillionitems/");
array.forEach(key, function() {
if ( questionRef.child(key) !== null ){
refID = questionRef.val();
result.push(refID);
}
})
The last one seems the nicest, the previous one seems a bit expensive on the old RAM.
However, I apparently have to call questionRef.once('value', function(){}) each time, hence already loading the whole document-root...
Or am I misunderstanding how Firebase handles these requests? is the .numChildren() just an answer directly from the server?
Is the .forEach actually remotely executed?
I'm wondering if there is any other way to reduce traffic per request. Which brings me to another question: it seems that firebase searches locally first, but eventually will search remotely, but it's not clear when this exactly happens. Does it periodically check if something has changed? Or will that only happend when I use .on() and not .once().
Or am I using the wrong backend for this purpose? Any other suggestions? I tried hood.ie which is still very beta, looked at Parse but firebase seemed to have the simplicity I need.
(sorry for the sloppy syntax, but you can see what I intended)
[update]
I now have this:
load: function(uids){
var FB = new Firebase(URL);
uids.map(function(uid) {
var currentRef = FB.child( uid+"/_current" );
currentRef.once('value', function (each) {
eachVal = each.val()
if (eachVal !== null){
var localSave = {};
localSave[uid] = eachVal;
this.saveLocal(localSave)
} else {
console.error("Not found: [%s]", uid)
}}, function (err) { });
});
}
But I'm still wondering when the request actually happens, on .child()? or in .once() and if the latter, what is the use of .child() exactly? It seems it's only used for referencing.
Then the second thing, if I want to retrieve an array of a hundred items, this would still mean a hundred seperate requests? or does Firebase have a way of collecting requests and then send them in a batch?
In that last case .once would be a more 'conservative' option for initial retrieval, then later you could attach a .on listener if you need real-time updates.

How to use HTML5 Local Storage with Ember.js?

I would like to use HTML5 Local Storage with my Ember.js.
I haven't been able to find any examples of doing this without Ember Data.
How should this be done? What do I need to consider?
So let's say we have an object called Storage that in our real-world implementation would represent an adapter-like object for the localStorage to store and retrieve data:
App.Storage = Ember.Object.extend({
init: function() {
this.clearStorage();
var items = ['foo', 'bar', 'baz'];
localStorage.items = JSON.stringify(items);
},
find: function(key) {
// pseudo implementation
if( !Ember.isNone(key) ) {
var items = [];
var storedItems = JSON.parse(localStorage[key]);
storedItems.forEach(function(item){
items.pushObject(item);
});
return items;
}
},
clearStorage: function() {
// pseudo implementation
localStorage.clear();
}
});
Beside the pseudo implementations, you can see there is a dummy array with some data stored at object initialization, we will use this later in our IndexRoute model hook to retrieve it, just to show that this works.
Now to the more nice stuff, you could do the register & inject directly after the application is ready, but what if we wanted it to be already available at application initialization? Well "there an ember-feature for that", called Application.initializer, initializer are simple classes with a 'name' property and a initialize function in where you have access to your application container and do what ever needs to be done, let me explain this in code:
To be notified when the application start loading we can listen to the onLoad event to create our initializer classes that will register and inject the before mentioned Storage object into every controller and every route:
Ember.onLoad('Ember.Application', function(Application) {
// Initializer for registering the Storage Object
Application.initializer({
name: "registerStorage",
initialize: function(container, application) {
application.register('storage:main', application.Storage, {singleton: true});
}
});
// Initializer for injecting the Storage Object
Application.initializer({
name: "injectStorage",
initialize: function(container, application) {
application.inject('controller', 'storage', 'storage:main');
application.inject('route', 'storage', 'storage:main');
}
});
});
Now, since the Storage object was injected into every route and every controller we can finally get access to it in our IndexRoute model hook and make the stores array mentioned above available trough the call self.get('storage').find('items') to our template to be rendered (just added a promise to make it actually conform with the ember-way and with some fictive delay, rather than just returning the array):
App.IndexRoute = Ember.Route.extend({
model: function(){
var self = this;
var promise = new Ember.RSVP.Promise(function(resolve) {
Ember.run.later(function() {
var data = self.get('storage').find('items');
console.log(data);
resolve(data);
}, 1000);
});
return promise;
}
});
In our index template we can now agnostically loop over the dummy array not caring where it is coming from:
<script type="text/x-handlebars" id="index">
<h2>Index</h2>
<ul>
{{#each item in model}}
<li>Item: {{item}}</li>
{{/each}}
</ul>
</script>
And lastly, you can see here all the above explained in a working example: http://jsbin.com/eqAfeP/2/edit
Hope it helps.
The accepted answer is great, but I thought I would add this alternative:
Dan Gebhardt has created a very interesting library called Orbit.js for coordinating different data sources on a client. There are three out of the box data sources: memory, local storage, and json api.
For ember integration, check out ember-orbit.
It is still under heavy development at this time, and it introduces a different paradigm than Ember Data, so proceed with caution!

Is it possible to reset a jQuery deferred object state?

Is it possible to reset a resolved jQuery object to an 'unresolved' state and kick off it's initialization and callbacks all over again?
The specific thing I'm doing is that I have a jQuery deferred wrapper over the local file system api. From there I build up higher level deferreds for the things I care about:
var getFs = defFs.requestQuota(PERSISTENT, 1024*1024)
.pipe (bytes) -> defFs.requestFs(PERSISTENT, bytes)
var getCacheContents = getFs.pipe (fileSystem) ->
defFs.getDirectory('Cache', fileSystem.root).pipe (dir) ->
defFs.readEntries(dir)
Now most of the time, when I call getCacheContents I don't mind the memo-ized values being returned, in fact I prefer it. But, on the occasion when I want to write to the cache I really would like the ability to reset that pipe and have it re-select and rescan the cache next time its accessed.
I could cobble something together from $.Callbacks but a deferred-based solution would really be ideal.
No. A Promise is by definition a thing that resolves only once - from unresolved to fulfilled OR to rejected. You will not be able to do this with jQuery's Deferreds.
What you are actually searching for are Signals. They are to be fired more than once, but provide a similiar interface. There are some implementations around, you might ceck out js-signals or wire.js.
The only solution I could find is to reset the $.Deferred object and return new Promise from that one. It works together with some internal API dirty checking (if something gets edited / deleted), but would be more performant to just reset the existing $.Deferred and let it re-resolve on the next Promise request.
An example of a possible solution is:
$.myDeferredList = [];
$.createRestorableDeferred = function(a,b) {
// JUST BY SIMPLE $.when().then();
$.myDeferredList[a] = {
deferred: $.Deferred()
, then: b
,restore : function() {
$.myDeferredList['myReady'].deferred = $.Deferred();
$.when($.myDeferredList['myReady'].deferred).then($.myDeferredList['myReady'].then);
}
,resolve : function() {
$.myDeferredList['myReady'].deferred.resolve();
}
}
$.when($.myDeferredList['myReady'].deferred).then($.myDeferredList['myReady'].then);
window[a] = $.myDeferredList['myReady'];
}
var counter = 0;
$.createRestorableDeferred('myReady', function () {
console.log('>> myReady WHEN called',++counter);
$.myDeferredList['myReady'].restore();
});
// RESOLVING ways
$.myDeferredList['myReady'].deferred.resolve();
$.myDeferredList.myReady.deferred.resolve();
myReady.resolve();
Results in console:
>> myReady WHEN called 1
>> myReady WHEN called 2
>> myReady WHEN called 3