有很長一段時間沒有更新博客了,近一段時間開始重新梳理知識點和寫博客了。重要的事情說三遍,新的博客地址:歡迎訪問,新的博客地址:歡迎訪問,新的博客地址:歡迎訪問。
回調對象Callbacks
回調對象Callbacks就是用來管理回調函數隊列的。
###參數說明###
它提供幾個便捷的處理參數
- once: 確保這個回調列表只執行一次
- memory: 保持以前的值,將添加到這個列表的後面的最新的值立即執行調用任何回調
- unique: 確保一次只能添加一個回調(所以在列表中沒有重複的回調).
- stopOnFalse: 當一個回調返回false 時中斷調用
once和stopOnFalse作用於fire
memory和unique作用於add
once在源碼中的實現:
// 以給定的上下文和參數調用所有回調函數
fireWith: function( context, args ) {
//第二次觸發的時候fired爲true
//stack = !options.once && []
//!fired爲false
//如果沒有設置once,!options.once && []爲true 則再次觸發fire( args );
//如果設置了once,!options.once && []爲false 則不會再次觸發fire( args );
if ( list && ( !fired || stack ) ) {
args = args || [];
//args.slice是判斷args是不是數組
args = [ context, args.slice ? args.slice() : args ];
if ( firing ) {//如果正在回調
//將參數推入堆棧,等待當前回調結束再調用
stack.push( args );
} else {//否則直接調用
fire( args );
}
}
return this;
}
memory在源碼中的實現:
// 存在memory就會再次觸發fire
else if ( memory ) {
//如果options.memory爲true,則將memory做爲參數,應用最近增加的回調函數
firingStart = start;
fire( memory );
}
unique在源碼中的實現:
options.unique->如果沒有設置unique,不管list中有沒有該回調都可以添加
options.unique->如果設置了unique,只有list中沒有纔可以添加該回調
if ( !options.unique || !self.has( arg ) ) {//確保是否可以重複
list.push( arg );
}
stopOnFalse在源碼中的實現:
//data[ 0 ]是執行環境
//data[ 1 ]就是fire中的參數
if ( list[ firingIndex ].apply( data[ 0 ], data[ 1 ] ) === false && options.stopOnFalse ) {
//阻止未來可能由於add所產生的回調
memory = false;
break;//由於options.stopOnFalse設置爲true,所以當有回調函數返回值爲false時退出循環
}
once_demo:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>once</title>
</head>
<body>
<script type="text/javascript" src="../jquery-2.1.1.js"></script>
<script type="text/javascript">
var cb = $.Callbacks("once");
function aaa(){
console.log(1);
}
function bbb(){
console.log(2);
}
cb.add(aaa);
cb.add(bbb);
cb.fire();//輸出1
cb.fire();//不執行
</script>
</body>
</html>
memory_demo:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>memory</title>
</head>
<body>
<script type="text/javascript" src="../jquery-2.1.1.js"></script>
<script type="text/javascript">
var cb = $.Callbacks("memory");
function aaa(){
console.log(1);
}
function bbb(){
console.log(2);
}
cb.add(aaa);
cb.fire();
cb.add(bbb);//由於memory有記憶功能,後添加的也能觸發
</script>
</body>
</html>
unique_demo:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>Unique</title>
</head>
<body>
<script type="text/javascript" src="../jquery-2.1.1.js"></script>
<script type="text/javascript">
var cb = $.Callbacks("unique");
function aaa(){
console.log(1);
}
cb.add(aaa);//有效
cb.add(aaa);//添加不進去
cb.fire();//輸出 1
</script>
</body>
</html>
源碼處理解析:
if ( !options.unique || !self.has( arg ) ) { //確保是否可以重複
list.push( arg );
}
如果options.unique爲true,並且回調列表中不包含arg,則將arg添加到回調列表中
stopOnFalse_demo:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>stopOnFalse</title>
</head>
<body>
<script type="text/javascript" src="../jquery-2.1.1.js"></script>
<script type="text/javascript">
var cb = $.Callbacks("stopOnFalse");
function aaa(){
console.log(1);
return false;
}
function bbb(){
console.log(2);
}
cb.add(aaa);//執行輸出1
cb.add(bbb);//不執行
cb.fire();
</script>
</body>
</html>
混合參數_demo:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>混合參數</title>
</head>
<body>
<script type="text/javascript" src="../jquery-2.1.1.js"></script>
<script type="text/javascript">
var cb = $.Callbacks("once memory");
function aaa(){
console.log(1);
}
function bbb(){
console.log(2);
}
cb.add(aaa);
cb.fire();//執行輸出1 2
cb.add(bbb);
cb.fire();//因爲once,不執行
</script>
</body>
</html>
###方法介紹 ##
根據jQuery.Callbacks()的API:
- callbacks.add() 回調列表中添加一個回調或回調的集合。
- callbacks.disable() 禁用回調列表中的回調
- callbacks.disabled() 確定回調列表是否已被禁用。
- callbacks.empty() 從列表中刪除所有的回調.
- callbacks.fire() 用給定的參數調用所有的回調
- callbacks.fired() 訪問給定的上下文和參數列表中的所有回調。
- callbacks.fireWith() 訪問給定的上下文和參數列表中的所有回調。
- callbacks.has() 確定列表中是否提供一個回調
- callbacks.lock() 鎖定當前狀態的回調列表。
- callbacks.locked() 確定回調列表是否已被鎖定。
- callbacks.remove() 從回調列表中的刪除一個回調或回調集合。
延遲對象Deferred
延遲對象是基於回調對象來實現的,用來實現異步的統一管理
when()是用來輔助延遲對象的
大體框架如下:
jQuery.extend({//兩個工具方法
Deferred:function(){},
when : function(){}
});
###$.Deferred()###
有三組對應關係:
- resolve->done
- reject->fail
- notify->progress
前兩組對應關係,分別對應相應的狀態變化:
- pending->resolved
- pending->rejected
一旦發生兩種狀態變化中的一種,則狀態變爲不可變
<body>
<script type="text/javascript" src="http://apps.bdimg.com/libs/jquery/2.1.4/jquery.min.js"></script>
<script type="text/javascript">
var cb = $.Callbacks();
setTimeout(function(){
alert(111);
cb.fire();
},1000);
//add先把這個函數存到數組裏
//當調用fire的時候再觸發
cb.add(function(){
alert(222);
});
</script>
<script type="text/javascript">
//延遲對象-實現對異步的統一管理
var dfd = $.Deferred();//延遲對象
setTimeout(function(){
alert(111);
dfd.resolve();
},1000);
dfd.done(function(){
alert(222);
});
</script>
<script type="text/javascript">
setTimeout(function(){
alert(111);
},1000);
//定時器就是異步的
//延遲對象可以實現從上往下的順序
//先彈出1,再彈出2
alert(222);
</script>
<script type="text/javascript">
var dfd = $.Deferred();
setTimeout(function(){
alert(111);
dfd.reject();
},1000);
dfd.fail(function(){
alert(222);
});
</script>
<script type="text/javascript">
var dfd = $.Deferred();
setTimeout(function(){
alert(111);
dfd.notify();
},1000);
dfd.progress(function(){
alert(222);
});
</script>
</body>
<body>
<script type="text/javascript" src="http://apps.bdimg.com/libs/jquery/2.1.4/jquery.min.js"></script>
<script type="text/javascript">
var def = $.Deferred();
setInterval(function(){
alert(111);
def.resolve();
// def.reject();
// def.notify();
},1000);
def.done(function(){
alert("成功");//只會彈一次成功
//因爲源碼中設置了jQuery.Callbacks("once memory")
}).fail(function(){
alert("失敗");//只會彈一次失敗
//因爲源碼中設置了jQuery.Callbacks("once memory")
}).progress(function(){
alert("進度中");//每隔一秒都會彈出
});
</script>
</body>
###deferred和promise的區別###
Deferred對象和promise對象都是延遲對象
####Deferred####
- resolve
- reject
- notify
- state
- always
- then
- promise
- pipe
- done
- fail
- progress
####promise對象#### - state
- always
- then
- promise
- pipe
- done
- fail
- progress
通過上面的列舉,我們可以看出:deferred比promise多了resolve、reject和notify
<body>
<script type="text/javascript" src="../../jquery-2.1.4.js"></script>
<script type="text/javascript">
function aaa(){
var dfd = $.Deferred();
setTimeout(function(){
dfd.resolve();//不會觸發
},1000);
return dfd;
}
var newDfd = aaa();
newDfd.done(function(){
alert("成功");
}).fail(function(){
alert("失敗");//彈出失敗
});
newDfd.reject();//會先觸發
</script>
</body>
dfd.promise():延遲對象調用promise()方法,返回一個promise對象
promise對象中沒有reject()方法
這裏爲了阻止延遲對象的狀態在外部被改變,需要這樣來調用dfd.promise()。它的作用是,在原來的deferred對象上返回promise對象,後者只開放與改變執行狀態無關的方法(比如done()方法和fail()方法),屏蔽與改變執行狀態有關的方法(比如resolve()方法和reject()方法),從而使得執行狀態不能被改變。
源碼中是這樣實現的:
dfd.promise()這裏參數爲空,所以這裏就返回promise對象
promise: function( obj ) {
return obj != null ? jQuery.extend( obj, promise ) : promise;
}
<body>
<script type="text/javascript" src="../../jquery-2.1.4.js"></script>
<script type="text/javascript">
function aaa(){
var dfd = $.Deferred();
setTimeout(function(){
dfd.resolve();
},1000);
return dfd.promise();
}
var newDfd = aaa();
newDfd.done(function(){
alert("成功");//彈出成功
}).fail(function(){
alert("失敗");
});
newDfd.reject();//會報錯 promise對象沒有改變狀態的方法
</script>
</body>
###always###
<body>
<script type="text/javascript" src="../../jquery-2.1.4.js"></script>
<script type="text/javascript">
var dfd = $.Deferred();
setTimeout(function(){
//dfd.resolve();
dfd.reject();
},1000);
//不管是resolve還是reject,always總會執行
dfd.always(function(){
alert("總會彈出");
});
</script>
</body>
源碼中是這樣寫的:
always: function() {
//總會觸發是因爲既有done又有fail
deferred.done( arguments ).fail( arguments );
return this;
//return this表示可以進行鏈式操作
}
###state###
返回狀態
resovled或者rejected
<body>
<script type="text/javascript" src="../../jquery-2.1.4.js"></script>
<script type="text/javascript">
function aaa(){
var dfd=$.Deferred();
alert(dfd.state());//pending
setTimeout(function(){
dfd.resolve();//resolve()之後,狀態變爲resolved
alert(dfd.state());//resolved
},1000);
return dfd;
}
var newDfd=aaa();
newDfd.done(function(){
alert('成功');
}).fail(function(){
alert('失敗');
});
</script>
</body>
###then###
<body>
<script type="text/javascript" src="../../jquery-2.1.4.js"></script>
<script type="text/javascript">
var dfd = $.Deferred();
setTimeout(function(){
//dfd.resolve();
dfd.reject();
},1000);
//then中對應三種狀態
dfd.then(function(){
alert("成功");
},function(){
alert("失敗");
},function(){
alert("進行中");
});
</script>
</body>
###pipe###
// then和pipe代碼一樣,功能不一樣
// pipe可以把延遲對象變長
promise.pipe = promise.then;
<body>
<script type="text/javascript" src="../../jquery-2.1.4.js"></script>
<script type="text/javascript">
var dfd = $.Deferred();
setTimeout(function(){
//dfd.resolve();
dfd.resolve('hello');//可以接收參數傳遞
},1000);
var newDfd = dfd.pipe(function(){
return arguments[0]+'你好';
});
newDfd.done(function(){
alert(arguments[0]);//hello你好
});
</script>
</body>
###$.when()###
when是用來輔助延遲對象的工具方法
<body>
<script type="text/javascript" src="../../jquery-2.1.4.js"></script>
<script type="text/javascript">
function aaa(){
var dfd = $.Deferred();
//dfd.resolve();
dfd.reject();
return dfd;
}
function bbb(){
var dfd = $.Deferred();
dfd.resolve();
//dfd.reject();
return dfd;
}
/*
這樣寫只有aaa()和bbb()同時完成觸發才彈出,但是任意一個未完成就可以觸發fail()
*/
//when是針對多個延遲對象的
$.when(aaa(),bbb()).done(function(){
alert('成功');
}).fail(function(){
alert('失敗');
});
</script>
</body>
<body>
<script type="text/javascript" src="../../jquery-2.1.4.js"></script>
<script type="text/javascript">
function aaa(){
var dfd = $.Deferred();
dfd.resolve();
return dfd;
}
function bbb(){
var dfd = $.Deferred();
dfd.reject();
return dfd; //這裏註銷後將返回成功,不註銷返回失敗
}
/*
這樣寫只有aaa()和bbb()同時完成觸發才彈出,但是任意一個未完成就可以觸發fail()
*/
//when是針對多個延遲對象的
//when中的參數必須是延遲對象才起作用
//如果不是延遲對象就會自動跳過該參數
$.when(aaa(),bbb()).done(function(){
alert('成功');
}).fail(function(){
alert('失敗');
});
</script>
</body>
$.when(aaa(),bbb(),ccc(),ddd()).done(function(){
alert('成功');
});
aaa()->arguments[0] done()
bbb()->arguments[1] done()
ccc()->arguments[2] done()
ddd()->arguments[3] done()
when是針對多個延遲對象的,比如這裏針對四個延遲對象,when的源碼中有一個計數器,這裏計數器就是4,觸發一個延遲對象,計數器減1,當計數器爲0時。$.when()會返回一個延遲對象deferred.promise(),然後觸發resolve()方法,最後觸發done。
相關閱讀博文: