How to create a callback priority queue with the help of jQuery?

callback, javascript, jquery, priority-queue, queue

Solution

I'd just suggest an array of callback objects that you could encapsulate in its own object. The object has methods for add, remove and execute. Add, adds a given callback in priority order. Remove finds a callback and removes it. Execute calls each callback in priority order, but stopping further execution of callbacks if any callback returns `false`.

Traversing the callbacks in priority order is simple (you just go from begin to end of the array) because the callbacks are kept in sorted order in the array.

function sortedCallbacks() {
    this.list = [];
}

sortedCallbacks.prototype = {
    // add callback to the list
    add: function(fn, priority, data) {
        var list = this.list;
        var callbackObj = {fn: fn, priority: priority, data: data};

        for (var i = 0; i < list.length; i++) {
            // if our new priority is greater, then insert it here
            if (priority > list[i].priority) {
                list.splice(i, 0, callbackObj);
                return;
            }
        }
        // if the list was either empty or no priorities in the list were less than
        // the new priority, then just add this onto the end of the list
        list.push(callbackObj);
    },
    // remove callback from the list
    remove: function(fn, priority) {
        var list = this.list;
        for (var i = 0; i < list.length; i++) {
            if (list[i].fn === fn && list[i].priority === priority) {
                list.splice(i, 1);
                return;
            }
        }
    },
    // change priority or data or both
    change: function(fn, oldPriority, newPriority, data) {
        this.remove(fn, oldPriority);
        this.add(fn, newPriority, data);
    }
    // execute callbacks in order
    execute: function(/* args for callback passed here */) {
        // execute in priority order
        var list = this.list, retVal, args;
        for (var i = 0; i < list.length; i++) {
            args = [].slice.call(arguments, 0);
            // add the data for this callback to any arguments that were passed to execute
            args.unshift(list[i].data);
            retVal = list[i].fn.apply(this, args);
            // if callback returns false, then stop further callback execution
            if (retVal === false) {
                return;
            }
        }        
    }
};

Sample usage:

// create callbacks object and add some callbacks to it
var callbacks = new sortedCallbacks();
callbacks.add(fn1, 4, "whatever");
callbacks.add(fn2, 9, "hello");
callbacks.add(fn8, -1, "nothing");

// execute the callbacks and pass them each a value
callbacks.execute(elem.value)

Problem

When doing `$(element).on('customevent',function(){});`, every element that binds the `customevent` will also trigger. This works roughly as a Pub/Sub, and it appears that there is no order or priority. What I want to do is, to implement my own callback queue, but, with ajustable priority (like what Wordpress has, you can set the hooks priorities). So a higher number have a bigger priority, and a number 0 will be a common and executed last. Given the example `$(element).on_priority(10, 'customevent', function(){});` and `$(element).on_priority(0, 'customevent', function(){});`, the `10` priority will execute the same `customevent` first, and might or not cancel the execution of the `0` priority event. How can I implement that? I'm having trouble trying to visualize the flow of the callbacks So far, I thought like this: - Keep a "global" (inside my closure) object with all the callback names set. - Inside each member of the callback, an object containing all the priorities. ``` var callbacks = { 'customevent': {}, 'customevent2': { 0: [{element: $element, fn: function(), data:{}}, {element: $element, fn: function(), data:{}}], 10: [{element: $element, fn: function(), data:{}}], 20: [{element: $element, fn: function(), data:{}}] } }; ``` Is the right direction? Performance wise it shouldn't be much of a hit, the problem is iterating the object over and over. The problem with this is that I wouldn't be able to unregister a previously set event or set the order (or even reorder) the callback array inside each priority

Original source