jQuery源碼學習:異步操做--Callbacks

jQuery封裝了不少關於異步操做的方法,好比$.ajax $.Deferred等等,這些方法都是以Callbacks爲基礎開發的,Callbacks一個多用途的回調列表對象,提供了強大的的方式來管理回調函數列表,他能夠管理一個列表,在你須要執行時候觸發,觸發時機由調用fire方法決定html

瞭解API

var cb = $.Callbacks();
cb.add(function(){
    console.log('first function');
})
cb.add(function () {
    console.log('second function');
})
cb.fire();
// first function
// second function
複製代碼

$.Callbacks()返回一個Callbacks對象,你能夠調用該對象的方法對函數列表進行操做,要實現這樣的功能很簡單,下面是一個簡易的實現ajax

function Callbakcs () {
    var list = []
    return {
        add: function (func) {
            list.push(func);
        },
        fire: function () {
            var args = [].slice.call(arguments);
            list.forEach(function(item) {
                item.apply(null, arguments);
            })
        }
    }
}
複製代碼

一樣的功能,只不過jQuery支持了不一樣的調用方式數組

參數

在調用$.Callbacks的時候,咱們能夠傳入一個參數用來改變回調列表的行爲緩存

  • once 確保改列表只執行一次,調用fire事後便失效
  • unique 一個回調只會被添加一次,不會重複添加
  • stopOnFalse 在某一個回調返回false時候當即中止執行後面函數
  • memory 緩存上一次fire的參數,下一次add的時候執行用上一次的參數調用
// 支持兩種傳參方式,一種對象類的,一種字符串類的,可組合使用,傳入字符串的時候會對字符串進行處理轉換爲對象
var cb = $.Callback("once unique");

var rnothtmlwhite = ( /[^\x20\t\r\n\f]+/g );

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

// 函數內部存儲了一個options對象,最後會是相似{once: true, unique: true}這樣的對象

複製代碼

變量說明

在函數內部咱們還能看到其餘一些變量bash

var //  標記是否正在觸發
	firing,

	//  緩存的上一次調用時候的參數和this指向
	memory,

	// 標記是否已經調用過fire
	fired,

	// 防止調用的標記
	locked,

	// 回調列表
	list = [],

	// 參數和this指向的列表
	queue = [],

	// 標記當前執行到哪一個回調
	firingIndex = -1,
	
	// 執行回調列表 
	fire = function () {}
	
	// 暴露的對象
	self = {}
複製代碼

add

add方法主要會判斷是不是unique、memory模式、unique遇到相同的回調函數回過濾掉,memory會檢查fired是否爲true,若是是會將memory裏面的參數添加到queue隊列中,最後調用fireapp

if ( memory && !firing ) {
    	firingIndex = list.length - 1;
    	queue.push( memory );
	}
	if ( memory && !firing ) {
    	firingIndex = list.length - 1;
    	queue.push( memory );
	}

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

複製代碼

添加的是否用到了遞歸,由於支持了傳入數組的方法異步

fire

self裏面會調用fireWith方法,咱們在調用fire的時候其實是調用的這個方法函數

fire: function() {
	self.fireWith( this, arguments );
	return this;
},
fireWith: function( context, args ) {
    if ( !locked ) {
    	args = args || [];
    	args = [ context, args.slice ? args.slice() : args ];
    	// 降參數和this對象緩存到queue裏面
    	queue.push( args );
    	if ( !firing ) {
    		fire();
    	}
    }
    return this;
},
複製代碼

真正的fire會遍歷取出queue裏面的參數,緩存到memory裏面,若是不是memory模式則會在後面清除掉,遍歷list,用memory裏的參數調用各個方法ui

for ( ; queue.length; firingIndex = -1 ) {
	memory = queue.shift();
	while ( ++firingIndex < list.length ) {

    // 若是傳入stopOnFalse,且返回false,當即中止遍歷
    if ( list[ firingIndex ].apply( memory[ 0 ], memory[ 1 ] ) === false &&
    	options.stopOnFalse ) {
    	firingIndex = list.length;
    	memory = false;
    }
	}
}
複製代碼

Callback部分的代碼比較簡單,固然這部分都是同步邏輯,callback是Deffered的基礎,因此須要先明白一下原理this

相關文章
相關標籤/搜索