• log out

Just some of Qbix's major features:

More features for advanced users:

There is much more to Qbix...

Building web apps with Qbix

Qbix comes with a "front end SDK" of sorts, in the form of files provided by plugins. These include javascript files, css files, images, etc.

Qbix will work with your favorite front-end libraries, whether they are Javascript (jQuery, Backbone, Angular, etc.) or CSS (Bootstrap, etc.) However, it also provides functionality that specifically empowers you to write better apps with Qbix.

Q.Cache

Would you like to be able to cache stuff in the document, localStorage or sessionStorage, while conserving memory and removing least-recently-used items? Well, Q.Cache was provided to do exactly this.

Q.getter

Let's say you have a function that sends a request to a server to retrieve some data. What you would usually like to do is:

  • Cache the data: if the same data is requested after the response arrived, just use the cache
  • Waiting list: if the same data was requested before the response arrived, don't send a duplicate request
  • Throttling: prevent too many requests from being sent at a time

To make any function become "smarter" and support all this, simply wrap it:

func = Q.getter(func); // func now rocks

Functions returned by Q.getter can be used to retrieve objects without worrying about whether they have alrady been cached, or if the request just went out, etc. The plugins that come with Q make great use of this. For example, to get a stream from the server you just always do:

Streams.get(publisherId, name, function () {
  console.log(this); // the stream, don't worry
})

Q.batcher

When an app is built in a modular way, you wind up having a bunch of components ("tools") on a page. Each of these components may want to request some objects from the server. While Q.getter definitely helps with not requesting the same object multiple times, we can do even better.

Q.batcher allows you to request multiple things at once. You'd usually like to send the batch request when either:

  • The current batch is full: we have hit the maximum number of objects we can request at once
  • A timeout occurs: we waited 50ms and there were no more requests for objects

Q.batcher takes a "batch function" which does the actual batch request, and returns a function that you would call normally, without worrying about the batching underneath. When you wrap the result in Q.getter as well, you get a function which you can use to call without worrying about

  • caching
  • waitlisting
  • throttling
  • batching

When you request that stream from the server using Streams.get(), it just works.

Q.Pipe

Often your code takes the form of:
"get these objects, wait until they have all been filled, and then proceed".

For this, you have Q.Pipe. Unlike Deferred or MultipleDeferred used in many libraries, the pipe.fill(field) method returns a function that can be used in any operation in place of regular callback function. For example:

var pipe = new Q.Pipe(['s1', 's2', 'u'],
function (subjects, args) {
	console.log(subjects.s1.name);
	console.log(subjects.s2.name);
	console.log(subjects.u.name);
});
// since Streams.get uses Q.getter,
// some of the following calls may
// call the callback synchronously,
// and others may issue a server
// request and call it asynchronously
Streams.get(p, n1, pipe.fill('s1'));
Streams.get(p, n2, pipe.fill('s2'));
Users.get(userId, pipe.fill('u'));

Sometimes, you need to execute a loop and build up the array of keys to wait for, because you don't know it up front. In this case, you'd do something like the following:

var pipe = new Q.Pipe();
var waiting = [];
Q.each(someCollection, function () {
  var key = this.foo + "\t" + this.bar;
  doSomething(this, pipe.fill(key));
  waiting.push(key);
});
pipe.add(waiting, 1, function _handler () {
  // the 1 means execute this at most once
}).run();
	

The reason pipe.run() is called at the end is that doSomething may have invoked its callback synchronously every time, so the pipe has been already filled, and waiting around is pointless. Just to make sure _handler isn't executed multiple times in case pipe.fill() is called again in the future, we put the 1 in front of it. We could have also just wrapped it with Q.once().

You can, of course, use pipe.fill(key) for error callbacks as well as success callbacks.