自製jQuery waitFor函式:等待條件成立執行作業
0 | 10,248 |
在寫JavaScript程式時,常有協調非同步作業的需求,像是作業B必須等待作業A完成後再進行,傳統做法多使用Callback函式,例如:
function doJobA(callback) {
//do something
callback();
}
function doJobB() {
//do another thing
}
doJobA(doJobB);
學會jQuery.Deferred後,我幾乎都改用Deferred處理非同步作業,不管串接多個作業循序執行[參考]或等待多項作業完成再進行,用Deferred都不難搞定。但有一些例外情況:等待對象不在我們掌握範圍(例如:位於其他Frame、由3rd Party程式控制),或是在完成時加入Callback呼叫的修改工程浩大,我多會選擇另一條捷徑-輪詢(Polling)大法!使用setInterval每隔一小段時間(例如:0.05秒)檢查前置作業是否完成,一發現完成就觸發後續作業。走捷徑要付點代價,輪詢法有兩項缺點:1) 即時性較差,前置作業完成後要等到下次檢查才觸發後續作業,最糟會延遲一次輪詢的時間間隔 2) 前置作業完成前的反覆檢查需消耗CPU資源(故要切記,檢查邏輯愈簡單愈好,例如複雜的jQuerySelector或AJAX查詢就不是好選擇)。雖然有這些缺點,但輪詢法很單純直覺,且能用在所有非同步作業上,在我心中就像散彈槍一樣好用,不管叢林裡有什麼妖魔鬼怪,不需費心瞄準,轟一聲,問題就解決了。 XD
因為三不五時拿出來打怪,索性寫成共用函式-jQuery.waitFor()方法。基於應用彈性,我設計了兩個參數:等待時限(timeout)以及輪詢間隔(checkInterval)。timeout用來停損,避免前置作業故障時還一直輪詢到海枯石爛;checkInterval則用來控制檢查頻率,間隔愈短即時性愈高,但愈消耗CPU,可視需求自行拿捏。寫非同步程式我已經離不開jQuery Deferred,所以jQuery.waitFor()骨子裡還是用jQuery Deferred實做,傳回的是Promise物件,接上done()指定後續作業,fail()則用來處理等待逾時。完整程式及應用範例如下:demo
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>waitFor函式</title>
</head>
<body>
<input type="button" id="b1" value="Start (5000ms)" />
<input type="button" id="b2" value="Start (1000ms)" />
<hr />
<input type="button" id="trg" value="Trigger" />
<hr />
<ul id="display"></ul>
<script src="//code.jquery.com/jquery-2.1.1.min.js"></script>
<script>
/**
* 條件成立時才執行
*
* @param check 檢查函式
* @param timeout 最長等待時間
* @param checkInterval 檢查間隔
*/
jQuery.waitFor = function(check, timeout, checkInterval) {
var dfd = jQuery.Deferred();
var checkHandle = setInterval(function () {
if (check()) {
clearInterval(checkHandle);
dfd.resolve();
}
}, checkInterval || 50);
var timeoutHandle = setTimeout(function () {
if (dfd.state() == "pending") {
clearInterval(checkHandle);
clearTimeout(timeoutHandle);
dfd.reject();
}
}, timeout || 5000);
return dfd.promise();
}
//測試程式
var $disp = $("#display");
function log(msg) {
var t = JSON.stringify(new Date()).split('T')[1].substr(0, 12);
$disp.append("<li>" + t + " " + msg + "</li>")
}
var state = false;
function init() {
state = false;
log("Start");
}
function trigger() {
state = true;
log("Trigger");
}
$("#b1").click(function() {
init();
$.waitFor(function() { return state; }, 5000)
.done(function() { log("Executed"); }).fail(function() { log("Timeout"); });
});
$("#b2").click(function() {
init();
$.waitFor(function() { return state; }, 1000)
.done(function() { log("Executed"); }).fail(function() { log("Timeout"); });
});
$("#trg").click(trigger);
</script>
</body>
</html>
以下為操作示範。每次測試時會先將state變數設為false,接著呼叫我們自訂的jQuery.waitFor(fuction() { return state; }),每隔50ms檢查一次state,當state為true就觸發done()邏輯,若超過1秒或5秒state都還是false,則判定逾時觸發fail()邏輯,而「Trigger」按鈕則用來將state變數設為true。在測試中,先測Timeout,按鈕後什麼都不做,在5秒及1秒後會出現Timeout訊息;第二回合則驗證一按下「Trigger」鈕,done()立即執行。
以上就是我的散彈槍製作手冊,祝大家打怪順利~ XD
Comments
Be the first to post a comment