【 以下文字转载自 Google 讨论区 】
发信人: modico (modico), 信区: Google
标 题: gmail client code review,part 9
发信站: 水木社区 (Sat Oct 21 17:06:27 2006), 站内
利用 setTimeout 的机制使用户界面不至于冻结,在代码中被广泛使用。在此基础上,google采取了一些额外的措施和限制. _setTimeout() 函数有3个参数,分别是window对象,函数指针,延迟时间。与标准方法比起来,多了第一个window对象的参数。但并不仅仅是为了把 window.setTimeout(func, delay) 换成 _setTimeout(window, func, delay)。func 这个参数在 _setTimeout 中不再能够使用字符串,必须是函数指针;func 参数在 _setTimeout 被裹了一个包装函数;window参数指向的窗口建立一个名为 _tm 的变量来管理 setTimeout 返回的id,每个id都在 _tm 里注册,以便在窗口卸载清理的时候,取消还未来得及执行的任务。(tm == timeout manager?)
值得注意的是,创建内部包装函数时并没有直接使用 window 对象,而是用了window.name 的方式来间接引用window对象,这大概是出于对内存泄漏问题的考虑。当任务被如期执行或是被取消时,相应的在 _tm 中的注册的id也要删除。被执行的任务,也就是包装函数,是如何得知自己对应的id的呢?当创建完包装函数后,_setTimeout 会将 setTimeout 返回的 id 作为属性附加到包装函数上,包装函数会在调用完func后取自己的id属性,然后在_tm中删除此id。
创建包装函数的做法,使得大多数情况下会出现两个 closures, 因为callback参数通常也是一个 closure. 是不是有必要呢?也许在跨窗口应用的情形下是有点道理的,如果窗口A的代码为窗口B建立了一个延时任务,而任务的执行代码是在A内,那么等到执行的时机到来的时候, 确实有必要要检查一下窗口B的状态,如是否已关闭等等。
_setTimeout 的参数设计最好是把 win 放在最后,不写时默认为本窗口,
因为把一个可选参数放在一个必需的参数前面,这种设计不太好。
function _setTimeout/*u*/(win, callback, delay)
{
if (! win) {
win = window;
}
if (! win._tm) {
win._tm = [];
}
var wrapper = internalTimeoutCallback/*tX*/(win.name, callback);
var timeoutId = win.setTimeout(wrapper, delay);
wrapper.id = timeoutId;
win._tm[timeoutId] = 1;
return timeoutId;
}
function internalTimeoutCallback/*tX*/(winName, callback)
{
var wrapper = function()
{
try {
var win = getWindow/*K*/(winName);
if (! win) {}
else {
if (win.closed) {}
else {
callback(win);
}
var e = win._tm;
if (e) {
delete e[wrapper.id];
}
}
}
catch (e) {
dumpException/*Gz*/(e);
}
};
return wrapper;
}
function _clearTimeout/*qa*/(win, timeoutId)
{
if (! win) {
win = window;
}
win.clearTimeout(timeoutId);
if (win._tm) {
delete win._tm[timeoutId];
}
}
function clearPenddingTimeouts/*kO*/(win)
{
if (win && win._tm) {
try {
for (var timeoutId in win._tm) {
win.clearTimeout(timeoutId);
}
win._tm = [];
}
catch (c) {
dumpException/*Gz*/(c);
}
}
}
--
FROM 221.218.167.*