gmail loading progress bar 实现原理

  Gmail 登陆时,会显示一个progress bar, 显示加载的进度。

最先以为是模拟的效果,但是仔细观察发现,进度条是真实反映加载以及下载进度的,并不依赖网络状况。

所以非常好奇,因为在javascript中缺少检测文档下载进度的ApI(js的安全机制也禁止这样做),且只提供了加载开始,加载中,加载完成(或加载错误)等状态。那么,gmail 是如何实现实时监控加载百分比的呢?

  用firebug 监控 gmail登陆时的文件下载,可以找到一个get请求,该请求返回html文本,文件大小为300多k,在其中找到这样一个函数:

 
  
function _B_prog(pct){
top[
" pr " ] = pct;
if (_B_thumbStyle_ === undefined) {
var thumb = top.document.getElementById( " lpt " );
_B_thumbStyle_
= thumb ? thumb.style : null
}
if (_B_thumbStyle_) {
_B_thumbStyle_.width
= Math.round(pct * 0.99 ) + " % " ;
if (pct == 100 )
_B_thumbStyle_
= null
}
}

该函数在html body 后定义。定义可知,进度条由该函数动态实现。

再来看后面大段大段的<script>标签,标签中是压缩的js,你可以在每一段<script>定义最末找到调用_B_prog(pct)的语句,并且参数是从1

一直到100,如下所示:

 
  
< script >
var JS_START_TIME = ( new Date).getTime(),GLOBALS = top.GLOBALS; if ( " o_6IqNZ5hNQ.zh_CN. " != GLOBALS[ 4 ])top.location.replace(top.location.href.split( " # " )[ 0 ]); function _B_log(imp,opt_val){ var p = " imp= " + imp; if (arguments.length > 1 )p += " &val= " + opt_val;_B_logImg_( " jsle " ,p)} var loadTimes = [GLOBALS[ 0 ],GLOBALS[ 1 ],JS_START_TIME]; function _B_record(){loadTimes.push(( new Date).getTime())} var _B_thumbStyle_;
function _B_prog(pct){top[ " pr " ] = pct; if (_B_thumbStyle_ === undefined){ var thumb = top.document.getElementById( " lpt " );_B_thumbStyle_ = thumb ? thumb.style: null } if (_B_thumbStyle_){_B_thumbStyle_.width = Math.round(pct * 0.99 ) + " % " ; if (pct == 100 )_B_thumbStyle_ = null }} function _B_err(e){ var state = loadTimes.join( " - " );_B_logImg_( " jserr " , " jsstate= " + encodeURIComponent(state) + " &jsmsg= " + encodeURIComponent(e));_B_handleError(e)} function _B_handleError(e){ throw e;}
function _B_logImg_(v,p){( new Image).src = " ?ui=2&view= " + v + " & " + p + " &ik= " + GLOBALS[ 9 ] + " &random= " + ( new Date).getTime()}window.onerror = function (message,url,line){_B_err(message)};
_B_prog(1 );
</ script >
 
  
< script >
  ....
function Ala(b){ var a = []; if (b) for (;b.isValidRow();)a.push(b.field( 0 )),b.next(); return a} function Bla(b){ return b && b.isValidRow() ? b.field( 0 ):l} function Cla(b){ if (b && b.isValidRow()){ for ( var a = {},c = b.fieldCount(),d = 0 ;d < c;d ++ )a[b.fieldName(d)] = b.field(d); return a} else return l} function Dla(b){ var a = []; if (b && b.isValidRow()) for ( var c = b.fieldCount(),d = 0 ;d < c;d ++ )a[d] = b.field(d); return a}
function Ela(b,a,c,d){ if (c.length == 0 || d >= c.length) return b.execute(a); else { if (ka(c[d])) return b.execute(a,c[d]);c = Array.prototype.slice.call(c,d); return b.execute(a,c)}} function Fla(b,a,c,d,e){b = Ela(b,a,d,e); try { return c(b)} finally {b && b.close()}} function Gla(b,a){ var c,d;c = a ? " ROLLBACK " : " COMMIT " ;d = a ? " beforerollback " : " beforecommit " ; var e = b.dispatchEvent( new yla(d)); if (e)b.ia.execute(c),b.ka = 0 ,d = a ? " rollback " : " commit " ,b.dispatchEvent( new yla(d)); return e}
function ll(b){a:{ var a = b.nb; if (b.Ma) if (b.ka == 0 ){b.Ba =! 1 ;b.dispatchEvent( new yla( " beforebegin " ));b.ia.execute( " BEGIN " + a);b.eb = Hla[a];b.ka = 1 ; try {b.dispatchEvent( new yla( " begin " ))} catch (c){b.ia.execute( " ROLLBACK " ),b.ka = 0 ,h(c)}b =! 0 ; break a} else b.Ba ? h(Error( " =106 " )):Hla[a] > b.eb ? h(Error( " =107 " )):b.ka ++ ;b =! 1 } return b} function ml(b){ if (b.Ma) if (b.ka <= 0 && h(Error( " =108 " )),b.ka == 1 ){ var a = Gla(b,b.Ba); return ! b.Ba && a} else b.ka -- ; return ! 1 } function Ila(){} function Jla(b,a){ this .aa = b; this .ea = a}
function Kla(b){ri( this ); this .Ta = b} var Lla = GLOBALS[ 0 ],Mla = GLOBALS[ 2 ],ol = GLOBALS[ 3 ],hf = GLOBALS[ 4 ],Nla = GLOBALS[ 5 ],pl = GLOBALS[ 6 ],ql = GLOBALS[ 7 ],Ola = GLOBALS[ 8 ],rl = GLOBALS[ 9 ],sl = GLOBALS[ 10 ],Pla = GLOBALS[ 11 ],Qla = GLOBALS[ 12 ],Rla = GLOBALS[ 14 ],tl = GLOBALS[ 15 ],ul = GLOBALS[ 16 ],Sla = GLOBALS[ 17 ],Tla = GLOBALS[ 18 ],vl = GLOBALS[ 19 ],Ula = GLOBALS[ 20 ],mja = GLOBALS[ 21 ],Vla = GLOBALS[ 22 ],Wla = GLOBALS[ 24 ],Xla = GLOBALS[ 25 ],Yla = GLOBALS[ 26 ],Zla = GLOBALS[ 27 ],ama = GLOBALS[ 28 ],bma = GLOBALS[ 29 ],cma = GLOBALS[ 30 ];
_B_prog(13 )} catch (e){_B_err(e)}
</ script >

。。。

一直到

 
  
< script >
  ...
hL.prototype.eR
= function $cvb(a,c){ var d = this .ha,e = [];e[ 0 ] = w().toString( 16 );e[ 2 ] = " 0 " ;e[ 18 ] = [a];e[ 24 ] = c;e = new Dr( " ^act " ,e); this .insertNode(mBa(d,e))};V(TLb,Er);
TLb.prototype.aa
= function $dvb(a,c){ var d = this .Kn.Ua.ic(); if (Zr(d) && a == " iu " ){vr( this .Kn.qj());d = this .Kn.Ua;Jh(d);gra(d);d = this .Kn;wr(d.Fe);d.Ua.Sc.ea(); try { for ( var e = 1 ,f;f = c[e];e ++ ){ var g = new Dr( " ^act " ,f),i = mBa(d.Ua,g);d.Fe.Jb(( new LC(g.Yr()[ 0 ])).dw()); var k = d.Fe,m = i.get().Yr()[ 0 ][ 17 ]; if (m) for ( var q = 0 ,u = j;u = m[q];q ++ )k.cO(u[ 8 ]) && (fb(m,q),q -- );d.Fe.insertNode(i)}d.Fe.Ba();d.aa =! 0 } finally { if (d.Ua.Sc.aa(),f = d.Fe,f.Se == d)f.Se = l} return ! 0 } return ! 1 };
TLb.prototype.ea
= function $evb(a){ var c = this .Kn.Ua.ic();Zr(c) && a == 0 && this .Kn.qj().dispatchEvent( " Oe " )};V(ULb,PAa);ULb.prototype.aa = function $fvb(){ return Zr( this .Yi.ic()) ? new hL( this .Yi):l};V(VLb,HCa);VLb.prototype.ea = function $gvb(a,c){ var d = a.getItem();c.yO = d.Sh( " ^act " )};VLb.prototype.aa = function $hvb(a){ if (a.yO){ var c = new S;a.yO ? Uj({Kc: " GV " ,title: " Buzz " },c):c.append( " &nbsp; " ); return c.toString()}};O(N.Ja(), " t " );R(N.Ja(), " t " );
_B_prog(
100 )} catch (e){_B_err(e)}
</ script >

结果很明显,进度条就是这样每加载一段js,就调用进度函数来显示进度。

但是,等等,通常加载html时,并不是一段接着一段下载并加载,而是,等整个文件下载完成后再加载执行,这样就没有实时监控下载进度的效果了。

关键就是在这里,gmail并没有使用静态资源供客户端下载,而是通过类似servlet技术返回html。

这里用到了一点服务器推的技术,就是在每个</script>标签结束处调用PrintWriter.flush(), 在该方法之后,服务器端输出流会马上推送至客服端,但并没有关闭输出流,下次还可以再次调用flush(), 这样就可实现一段接一段将资源推送至客户端,实现进度监控了,而不是像目前webQQ一样模拟从0% ,然后等待,然后突然升到100%完成。

搞定,休息!

转载于:https://www.cnblogs.com/jinker/archive/2011/05/23/2054829.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值