JQuery source code series (VII) callbacks function

Time:2022-1-5

WelcomeMy columnView a series of articles.

To be honest, the source code of sizzle is so depressing that it was very painful during the period of writing sizzle articles. At first, I thought it was very interesting. The later I felt that the code was difficult to read and annoying.

After the winter vacation, I stayed at home for two weeks. I felt good. During this period, I forgot everything I learned and learned very little. I finished watching CCTV’s general history of China series. It is also a kind of peace of mind for me who is a history fan.

JQuery source code series (VII) callbacks function

When today’s topic is away, sizzle pauses for two weeks and feels a lot sober. Most of the pain previously dominated by sizzle has been eliminated. Today, let’s introduce the callbacks function of jQuery.

Use of callbacks

JQuery internally provides many methods for basic functions, such as $ ajax()、$. Each () and $ Callbacks (), these methods can be used internally or externally by developers.

There are several main methods supported by callbacks, such as add, fire, remove and disable. For example, there is an official example:

//These two are used as the callback function
function fn1( value ) {
  console.log( value );
}
 
function fn2( value ) {
  fn1("fn2 says: " + value);
  return false;
}

//Call jQuery's callbacks to generate callbacks
var callbacks = $.Callbacks();
callbacks.add( fn1 );

callbacks.fire( "foo!" );
// 'foo!'
 
callbacks.add( fn2 );
 
callbacks.fire( "bar!" );
// 'bar!'
// 'fn2 says: bar!'

As can be seen from the basic demo,$.Callbacks()Function generates a callbacks object whose.add()The method is to add a callback function, and.fire()The method is to execute the callback function.

.remove()To remove the callback function:

var callbacks = $.Callbacks();
callbacks.add( fn1 );

callbacks.fire( "foo!" );
// 'foo!'
 
callbacks.add( fn2 );

callbacks.fire( "bar!" );
// 'bar!'
// 'fn2 says: bar!'

callbacks.remove( fn2 );

callbacks.fire( "foobar" );
// 'foobar'

$.Callbacks()Several parameters are also supported to indicate several effects of callback execution,$.Callbacks('once')

  • once: make sure that this callback list only executes Fire() once (like a deferred deferred)

  • memory: keep the previous value, add the latest value to the back of this list, and call any callback immediately (like a deferred deferred)

  • unique: ensure that only one callback can be added at a time (so there are no duplicate callbacks in the list)

  • stopOnFalse: interrupt the call when a callback returns false

This method also supports multiple parameters, such as$.Callbacks('once memory')Please refer to this for specific uselink

Callbacks source code

Before putting the source code of jQuery 3.0, let’s simply simulate the callbacks function to realize its basic functions:

var Callbacks = function(){
  var Cb = {
    callbacks: [],
    add: function(fn){
      this.callbacks.push(fn);
      return this;
    },
    fire: function(value){
      this.callbacks.forEach(function(fn){
        fn(value);
      });
      return this;
    }  
  }
  return Cb;
}

//Testing
var callbacks = Callbacks();
callbacks.add(fn1);
callbacks.fire('test'); //'test'

You can see that a simple callbacks function is very simple to implement.

The whole callbacks source code is roughly as follows:

jQuery.Callbacks = function(options){
  //First process the parameters, such as once and unique
  options = createOptions(options);

  //Parameter definitions, including some flag and callback arrays
  var list = [], queue = [] ...

  //Fire is to traverse the array and return the execution of the function
  var fire = function(){
    ...
  }

  //Self is the final returned object
  var self = {
    add: function(){...},
    remove: function(){...},
    has: function(){...},
    disable: function(){...},
    fireWith: function(){...},// This is actually the execution of the fire function
    fire: function(){...}
    ...
  }
  return self;
}

Because we have briefly introduced how to implement a basic callback function, here is a little clearer. Let’s take a lookcreateOptionsFunction, which is mainly used for functions similar to$.Callbacks('once memory')Type flag separation for callback:

function createOptions(options) {
  var object = {};
  jQuery.each(options.match(rnothtmlwhite) || [], function (_, flag) {
    object[flag] = true;
  });
  return object;
}

amongrnothtmlwhiteIs a regular expression/[^\x20\t\r\n\f]+/gTo get all the flag flags. The result of createoptions is an object, and the key values are flag and Boolean respectively.

So the main problem now is all on those flags,"once memory unique stopOnFalse"

Source code:

jQuery.Callbacks = function(options) {
  //Flag processing
  options = typeof options === "string" ? createOptions(options) : jQuery.extend({}, options);

  var // Flag to know if list is currently firing
  firing,
    // Last fire value for non-forgettable lists
    memory,
    // Flag to know if list was already fired
    fired,
    // Flag to prevent firing
    locked,
    // Actual callback list
    list = [],
    // Queue of execution data for repeatable lists
    queue = [],
    // Index of currently firing callback (modified by add/remove as needed)
    firingIndex = -1,
    // Fire callbacks
    fire = function() {
      //Only execute it once, and it will not be executed in the future
      locked = locked || options.once;

      // Execute callbacks for all pending executions,
      // respecting firingIndex overrides and runtime changes
      fired = firing = true;
      for (; queue.length; firingIndex = -1) {
        memory = queue.shift();
        while (++firingIndex < list.length) {
          //The callback executes the function, checks for stoponfalse, and prevents it from continuing
          if (list[firingIndex].apply(memory[0], memory[1]) === false && options.stopOnFalse) {
            // Jump to end and forget the data so .add doesn't re-fire
            firingIndex = list.length;
            memory = false;
          }
        }
      }

      // Forget the data if we're done with it
      if (!options.memory) {
        memory = false;
      }

      firing = false;

      //Locked is implemented here
      if (locked) {
        //Although it is locked, it is memory. Keep the list for future use
        if (memory) {
          list = [];
          //Bye
        } else {
          list = "";
        }
      }
    },
    // Actual Callbacks object
    self = {
      // Add a callback or a collection of callbacks to the list
      add: function() {
        if (list) {
          // If we have memory from a past run, we should fire after adding
          if (memory && !firing) {
            firingIndex = list.length - 1;
            queue.push(memory);
          }

          (function add(args) {
            jQuery.each(args, function(_, arg) {
              if (jQuery.isFunction(arg)) {
                if (!options.unique || !self.has(arg)) {
                  list.push(arg);
                }
              } else if (arg && arg.length && jQuery.type(arg) !== "string") {
                // Inspect recursively
                add(arg);
              }
            });
          })(arguments);

          if (memory && !firing) {
            fire();
          }
        }
        return this;
      },
      // Remove a callback from the list
      remove: function() {
        jQuery.each(arguments, function(_, arg) {
          var index;
          while ((index = jQuery.inArray(arg, list, index)) > -1) {
            list.splice(index, 1);

            // Handle firing indexes
            if (index <= firingIndex) {
              firingIndex--;
            }
          }
        });
        return this;
      },
      // Check if a given callback is in the list.
      // If no argument is given, return whether or not list has callbacks attached.
      has: function(fn) {
        return fn ? jQuery.inArray(fn, list) > -1 : list.length > 0;
      },
      // Remove all callbacks from the list
      empty: function() {
        if (list) {
          list = [];
        }
        return this;
      },
      // Disable .fire and .add
      // Abort any current/pending executions
      // Clear all callbacks and values
      disable: function() {
        locked = queue = [];
        list = memory = "";
        return this;
      },
      disabled: function() {
        return !list;
      },
      // Disable .fire
      // Also disable .add unless we have memory (since it would have no effect)
      // Abort any pending executions
      lock: function() {
        locked = queue = [];
        if (!memory && !firing) {
          list = memory = "";
        }
        return this;
      },
      locked: function() {
        return !!locked;
      },
      // Call all callbacks with the given context and arguments
      fireWith: function(context, args) {
        if (!locked) {
          args = args || [];
          args = [context, args.slice ? args.slice() : args];
          queue.push(args);
          if (!firing) {
            fire();
          }
        }
        return this;
      },
      // Call all the callbacks with the given arguments
      fire: function() {
        self.fireWith(this, arguments);
        return this;
      },
      // To know if the callbacks have already been called at least once
      fired: function() {
        return !!fired;
      }
    };

  return self;
};

Generally speaking, the code of this pub / sub mode is easy to understand. There are some questions. For example, there are actually two arrays in the source code. List is a queue array, which should be called queue, but the queue array has been defined, and the function of queue is to store parameters during fire execution. This can not be confused.

In addition, when the whole code fires this parameter, the queue element needs to be supplemented when the function is running, that is, when fire is executed twice, butfire()The function executes only once.

summary

jQuery. Callbacks follows the usual routine of jQuery, and finallyreturn self, when I first saw it for the second time, it was a little vague. It was mainly the once, memory and other flag parameters that interfered with my line of sight, especially the implementation of these flag flags.

reference resources

jQuery. Callbacks Chinese document

JQuery 2.0.3 source code analysis callback object – callbacks

This paper is based on GitHubSource addressWelcome to star.

WelcomeMy blogcommunication.