When would I use JQuery.Callbacks?

南楼画角 提交于 2019-12-02 16:05:11
Neal

To expand on @Rockets answer a bit and clear up some confusion:

The reason that one might need to use jQuery's $.Callbacks is multifaceted:

  1. The user has a lot of code in one function and wants to split it up
  2. They take that information and send it through the jQuery callback function which then allows them to have split their code into better manageable pieces with which to work with.
    So (for example) if you look at @Rocket's code:

    var clickCallbacks = $.Callbacks();
    
    clickCallbacks.add(function() { //one one function piece
        //parse and do something on the scope of `this`
        var c = parseInt(this.text(), 10);
        this.text(c + 1);
    });
    clickCallbacks.add(function(id) { //add a second non-related function piece
        //do something with the arguments that were passed
        $('span', '#last').text(id);
    });
    
    $('.click').click(function() {
        var $ele = $(this).next('div').find('[id^="clickCount"]');
        clickCallbacks.fireWith($ele, [this.id]); //do two separate but related things.
    });
    
  3. What you can now have is multiple callback batches of function which you can now call whenever you deem it be necessary without the need to make so many changes throughout out your code.

I can see callbacks being useful when you are updating different DOM elements using the same method(s).

Here is a cheesy example: http://jsfiddle.net/UX5Ln/

var clickCallbacks = $.Callbacks();

clickCallbacks.add(function() {
    var c = parseInt(this.text(), 10);
    this.text(c + 1);
});
clickCallbacks.add(function(id) {
    $('span', '#last').text(id);
});

$('.click').click(function() {
    var $ele = $(this).next('div').find('[id^="clickCount"]');
    clickCallbacks.fireWith($ele, [this.id]);
});

It updates the click counter and the 'last clicked' when you click on something.

edsioufi

An (almost) out of the box jQuery Pub/Sub system

This is IMHO the most interesting application, and since it was not clearly stated in the answers (although some do make allusion to the usage), I am adding it to this relatively old post.

NB: The usage is clearly indicated, with an example, in the jQuery docs, but I guess it was added after the question was posted.

Pub/sub, aka the observer pattern, is a pattern which promotes loose coupling and single responsibility in an application. Rather than having objects calling directly the methods of other objects, objects instead subscribe to a specific task or activity and are notified when it occurs. For a more detailed explanation of the benefits of using the Pub/Sub pattern, you can check Why would one use the Publish/Subscribe pattern (in JS/jQuery)?.

Sure, this was possible with custom events using trigger, .on() and .off(), but I find jQuery.Callbacks to be a lot more suitable to the task, producing cleaner code.

Here is the example snippet from the jQuery documentation:

var topics = {};

jQuery.Topic = function( id ) {
    var callbacks,
        method,
        topic = id && topics[ id ];

    if ( !topic ) {
        callbacks = jQuery.Callbacks();
        topic = {
            publish: callbacks.fire,
            subscribe: callbacks.add,
            unsubscribe: callbacks.remove
        };
        if ( id ) {
            topics[ id ] = topic;
        }
    }
    return topic;
};

And a usage example:

// Subscribers
$.Topic( "mailArrived" ).subscribe( fn1 );
$.Topic( "mailArrived" ).subscribe( fn2 );
$.Topic( "mailSent" ).subscribe( fn1 );

// Publisher
$.Topic( "mailArrived" ).publish( "hello world!" );
$.Topic( "mailSent" ).publish( "woo! mail!" );

// Here, "hello world!" gets pushed to fn1 and fn2
// when the "mailArrived" notification is published
// with "woo! mail!" also being pushed to fn1 when
// the "mailSent" notification is published.

/*
output:
hello world!
fn2 says: hello world!
woo! mail!
*/

It seems that $.Callbacks began as an implementation detail: a means to manage lists of functions and to call all the functions in a given list with the same arguments. A little like C#'s multicast delegates, with additional features, like the flags you can pass to customize the list's behavior.

A good example might be that jQuery uses $.Callbacks internally to implement its ready event. bindReady() initializes a callback list:

readyList = jQuery.Callbacks( "once memory" );

Note the once and memory flags, that ensure the callback list will only be called once, and that functions added after the list has been called will be called immediately.

Then, ready() adds the specified handler to that list:

ready: function( fn ) {
    // Attach the listeners
    jQuery.bindReady();

    // Add the callback
    readyList.add( fn );

    return this;
}

Finally, the callback list is fired when the DOM is ready:

readyList.fireWith( document, [ jQuery ] );

All the ready handlers are called in the context of the same document with the same reference to the global jQuery object. They can only be called this once, and additional handlers passed to ready() will be called immediately from then on, all of this courtesy of $.Callbacks.

I don't see any specific mention of setting context, but since you can pass an arbitrary number of arguments, that would potentially be useful. You could also make your own convention to pass a flag as a first argument and have listeners return false immediately if they aren't meant to handle the remaining argument list.

I've encountered cases where something like this might have been useful, but have used bind() and trigger() with custom events instead. Imagine some message handling system (a web based chat room or e-mail client) where you're polling a service for new messages. One function might be setting a number in a span or displaying a growl when something happens. Another might be updating a grid. With triggers you would have to trigger the event for each listener and "unroll" the passed arguments from eventData, with callbacks it's just one fire and your listeners are simple javascript functions with a simple argument list.

Callbacks isn't exactly revolutionary, but it'll make for less and cleaner code.

I'm working on an app with a lot of business logic and at least 11 external services. It really helps keep things straight if you can write your own flow control classes and behaviors using something like Callbacks instead of trying to force your will on the Deferred implementation.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!