原文:http://dean.edwards.name/weblog/2009/03/callbacks-vs-events/
先看如下代码:
document.addEventListener("DOMContentLoaded", function() {
console.log("Init: 1");
DOES_NOT_EXIST++; // error
}, false); document.addEventListener("DOMContentLoaded", function() {
console.log("Init: 2");
}, false);
你预期当页面加载后,console下会出现什么结果?
结果是这样的:
Init: 1 Uncaught ReferenceError: DOES_NOT_EXIST is not defined Init: 2
重点在于: 两个事件监听函数都执行了.虽然在第一个事件监听函数中出现了错误,但并没有阻止第二个函数的执行.
问题来了.
接下来我们基于回调函数系统的代码.使用jQuery:
$(document).ready(function() {
console.log("Init: 1");
DOES_NOT_EXIST++; // error
}); $(document).ready(function() {
console.log("Init: 2");
});
此时你从console下看到了什么?没错,是这样:
Init: 1
Uncaught ReferenceError: DOES_NOT_EXIST is not defined
好吧,这意味着回调函数系统是极其脆弱的.一旦任何一个回调函数中抛出了异常,则余下的回调函数序列将不再执行.
在实际开发环境中,这意味着一个写得烂的插件可以令其他插件无法初始化.
Dojo与jQuery有相同的问题,而YUI包装了try/catch机制,它会让回调函数中的错误悄悄地被捕获:
YAHOO.util.Event.onDOMReady(function() {
console.log("Init: 1");
DOES_NOT_EXIST++; // this will throw an error
}); YAHOO.util.Event.onDOMReady(function() {
console.log("Init: 2");
});
所以你将在console看到如下结果:
Init: 1 Init: 2
几近完美的初始化! 貌似没什么好担心的了,除了那些你看不到的错误.
那该如何解决呢?
下面的解决方案是这样的: 使用回调函数混合真正的事件调度.
我们可以触发一个自定义事件,并在该事件的监听函数中,迂回地执行回调函数.
因为每个事件处理程序都有它自己的上下文,所以,即便在事件处理函数内发生了错误,也不会影响到我们的回调函数系统了.
回调函数序列中的每一个函数都将被执行.
这里是代码:
var currentHandler; if (document.addEventListener) {
document.addEventListener("fakeEvents", function() {
// execute the callback
currentHandler();
}, false); var dispatchFakeEvent = function() {
var fakeEvent = document.createEvent("UIEvents");
fakeEvent.initEvent("fakeEvents", false, false);
document.dispatchEvent(fakeEvent);
};
} else { // MSIE document.documentElement.fakeEvents = 0; // an expando property document.documentElement.attachEvent("onpropertychange", function(event) {
if (event.propertyName == "fakeEvents") {
// execute the callback
currentHandler();
}
}); dispatchFakeEvent = function(handler) {
// fire the propertychange event
document.documentElement.fakeEvents++;
};
} var onLoadHandlers = [];
function addOnLoad(handler) {
onLoadHandlers.push(handler);
}; window.onload = function() {
for (var i = 0; i < onLoadHandlers.length; i++) {
currentHandler = onLoadHandlers[i];
dispatchFakeEvent();
}
};
这次,执行结果当然又是我们预期的了:
Init: 1 Uncaught ReferenceError: DOES_NOT_EXIST is not defined Init: 2