looking for a full-immersion/face-to-face HTML5, JavaScript, and Mobile Web Development training in the heart of London? Book your spot
Showing posts with label API. Show all posts
Showing posts with label API. Show all posts

Friday, June 08, 2012

Asynchronous Storage For All Browsers

I have finally implemented and successfully tested the IndexedDB fallback for Firefox so that now every browser, old or new, should be able to use this interface borrowed from localStorage API but made asynchronous.

Asynchronous Key/Value Pairs

The main purpose of this asyncStorage API is to store large amount of data as string, including base64 version of images or other files.
As it is, usually, values are the bottleneck, RAM consumption speaking, while keys are rarely such big problem.
However, while keys are retrieved asynchronously and in a non-blocking way, but kept in memory, respective values are always retrieved asynchronously in order to do not fill the available amount of RAM for our Web Application.

Database Creation/Connection

Nothing more than ...

asyncStorage.create("my_db_name", function (db, numberOfItems) {
// do stuff with the asyncStorage
});


Storing An Item

As it is for localStorage, but async

asyncStorage.create("my_db_name", function (db, numberOfItems) {
db.setItem("a", "first entry", function () {
// done, item stored
});
});

Please note that if the item was there already, it's simply replaced with the new value.

Getting An Item

As it is for localStorage, but async

asyncStorage.create("my_db_name", function (db, numberOfItems) {
db.getItem("a", function (value) {
// done, value retrieved
});
});

Please note that if the item was not previously stored, the returned value will be exactly null as it is for localStorage.

Removing An Item

As it is for localStorage, but async

asyncStorage.create("my_db_name", function (db, numberOfItems) {
db.removeItem("a", function () {
// done, no key a is present anymore
// so length is decreased already here
// and db.get("a") will send a null value
});
});


Removing All Items

As it is for localStorage, but async, and considering that only values in the specified database name will be erased, rather than all of them.

asyncStorage.create("my_db_name", function (db, numberOfItems) {
db.clear(function () {
// done, database "my_db_name" is now empty
});
});


Getting All Items Keys

If this is really what you need to do, bear in mind the API is the same used in the localStorage where indeed there's no way to retrieve all keys if not doing something like:

for (var
keys = [],
i = db.length;
i--;
) {
keys[i] = db.key(i);
}


Getting All Items

Well, the thing here is that you can store an entire object through JSON so if you need to save and get back everything, it's kinda pointless to store different keys, just use one.
However, this is how I would do this task:

for (var
object = {},
complete = function () {
alert("Done, all items in the object");
},
ongetitem = function (value, key) {
object[key] = value;
if (!--j) complete();
},
i = db.length,
j = i;
i--;
) {
db.getItem(db.key(i), ongetitem);
}


On Github, Of Course

You can find full API description in this repository. Please forgive me if the name was initially db.js, I believe the new one, asyncStorage.js, is much more appropriate (also another guy created a script called db.js so ... well, I have avoided conflicts with that library).

That's Pretty Much It

And I hope you'll start using this API to avoid blocking mobile and desktop browsers when you store a lot of data ;)

Monday, January 16, 2012

On EventEmitter In node.js

Today I had a whole node.js session and I have spent a bit of time looking at current EventEmitter implementation.
I have twitted already that it sucks as it is, and while it's really trivial to implement the same for any browser, I believe many mistakes have been made about the API. Here the list:
  1. on() is a shortcut for addListener but there is no off() as removeListener's shortcut (inconsistent)
  2. add/removeListener is not DOM friendly, we cannot reuse an EventEmitter in the browser without double checking if those methods exist
  3. removeAllListeners() accepts no arguments and cleans up the whole emitter but there is no way to retrieve all listeners via listeners() passing no arguments (again, inconsistent)
  4. there's no possibility to use handleEvent property, as already defined in the EventListener, once again objects are not reusable between client and server
  5. no duplicated checks for both addListener and removeListener, this is totally inconsistent against DOM EventTarget plus I found it kinda hilarious since there is also a max number of listeners allowed defined via setMaxListeners method ... if we add same listener twice, it's fired twice ... a non-sense for those coming from the web. We also need to removeListener twice or more 'cause we have no idea if same listeners has been added by mistake twice so ... it's just screwed up, removing a specific listener may be not enough and there is no easy way to check if the listener has been removed or not
  6. there is no interface to define events plus the single argument is not a DOM Event like, not even an object with data property and at least a type that points to the event name ... once again, not possible to reuse anything on DOM world


Why All Of This Is Bad

node.js is bringing a lot of non JavaScripters and non browser friendly JS developers into its community and this is the good part. What is absolutely bad is that if node.js won't be minimally aligned with the rest of the code in the browsers out there our life as "one language everywhere" will become harder than ever.
I have personally created wru which runs in all browsers and many server side JS environments but what I would like to avoid is to write twice any sort of test because of weirdly implemented APIs.
If it's about shortcutting, as example, on() and off() are more than welcome but why on earth the long version should be addListener rather than addEventListener?
Why events passed as objects since ever in client JS should be passed as string with optional extra data as second argument?
Where are defaults control over events fired across more listeners?
Why on earth handleEvent is not supported and a listener can be only a function which most likely will require a bind to something else?
Why is it possible to erase all listeners without even knowing "who set them where" but it's not possible to retrieve all of them so that at least we could act accordingly with the event name following some rule rather than "just remove them all" ?

I hope somebody will agree with me, considering flakey points I have already described and changing or improving ASAP this EventEmitter API ... it would be sooooooo coooool to be aligned in both worlds for at least the most used pattern/procedure ever in JS world, don't you say? Thanks for your attention.