深入理解JavaScript定时机制

2011年11月7日 没有评论

本文系转载,原文出处http://www.laruence.com/2009/09/23/1089.html

JavaScript的setTimeout与setInterval是两个很容易欺骗别人感情的方法,因为我们开始常常以为调用了就会按既定的方式执行, 我想不少人都深有同感, 例如:

setTimeout(function() {
    alert('你好!');
}, 0);
setInterval(callbackFunction, 100);

认为setTimeout中的问候方法会立即被执行,因为这并不是凭空而说,而是JavaScript API文档明确定义第二个参数意义为隔多少毫秒后,回调方法就会被执行. 这里设成0毫秒,理所当然就立即被执行了.

同理对setInterval的callbackFunction方法每间隔100毫秒就立即被执行深信不疑!

但随着JavaScript应用开发经验不断的增加和丰富,有一天你发现了一段怪异的代码而百思不得其解:

div.onclick = function(){
        setTimeout(function() {
                document.getElementById('inputField').focus();
        }, 0);
};

既然是0毫秒后执行,那么还用setTimeout干什么, 此刻, 坚定的信念已开始动摇.

直到最后某一天 , 你不小心写了一段糟糕的代码:

setTimeout(function() {
        while (true) {
        }
}, 100);
setTimeout(function() {
        alert('你好!');
}, 200);
setInterval(callbackFunction, 200);

第一行代码进入了死循环,但不久你就会发现,第二,第三行并不是预料中的事情,alert问候未见出现,callbacKFunction也杳无音讯!

这时你彻底迷惘了,这种情景是难以接受的,因为改变长久以来既定的认知去接受新思想的过程是痛苦的,但情事实摆在眼前,对JavaScript真理的探求并不会因为痛苦而停止,下面让我们来展开JavaScript线程和定时器探索之旅!

拔开云雾见月明,出现上面所有误区的最主要一个原因是:潜意识中认为,JavaScript引擎有多个线程在执行,JavaScript的定时器回调函数是异步执行的.

而事实上的,JavaScript使用了障眼法,在多数时候骗过了我们的眼睛,这里背光得澄清一个事实:

JavaScript引擎是单线程运行的,浏览器无论在什么时候都只且只有一个线程在运行JavaScript程序.

JavaScript引擎用单线程运行也是有意义的,单线程不必理会线程同步这些复杂的问题,问题得到简化.

那么单线程的JavaScript引擎是怎么配合浏览器内核处理这些定时器和响应浏览器事件的呢?下面结合浏览器内核处理方式简单说明.

浏览器内核实现允许多个线程异步执行,这些线程在内核制控下相互配合以保持同步.假如某一浏览器内核的实现至少有三个常驻线程:javascript引擎线程,界面渲染线程,浏览器事件触发线程,除些以外,也有一些执行完就终止的线程,如Http请求线程,这些异步线程都会产生不同的异步事件,下面通过一个图来阐明单线程的JavaScript引擎与另外那些线程是怎样互动通信的.虽然每个浏览器内核实现细节不同,但这其中的调用原理都是大同小异.

由图可看出,浏览器中的JavaScript引擎是基于事件驱动的,这里的事件可看作是浏览器派给它的各种任务,这些任务可以源自 JavaScript引擎当前执行的代码块,如调用setTimeout添加一个任务,也可来自浏览器内核的其它线程,如界面元素鼠标点击事件,定时触发器时间到达通知,异步请求状态变更通知等.从代码角度看来任务实体就是各种回调函数,JavaScript引擎一直等待着任务队列中任务的到来.由于单线程关系,这些任务得进行排队,一个接着一个被引擎处理.

上图t1-t2..tn表示不同的时间点,tn下面对应的小方块代表该时间点的任务,假设现在是t1时刻,引擎运行在t1对应的任务方块代码内,在这个时间点内,我们来描述一下浏览器内核其它线程的状态.

t1时刻,GUI渲染线程:

该线程负责渲染浏览器界面HTML元素,当界面需要重绘(Repaint)或由于某种操作引发回流(reflow)时,该线程就会执行.本文虽然重点解释JavaScript定时机制,但这时有必要说说渲染线程,因为该线程与JavaScript引擎线程是互斥的,这容易理解,因为 JavaScript脚本是可操纵DOM元素,在修改这些元素属性同时渲染界面,那么渲染线程前后获得的元素数据就可能不一致了.

在JavaScript引擎运行脚本期间,浏览器渲染线程都是处于挂起状态的,也就是说被”冻结”了.

所以,在脚本中执行对界面进行更新操作,如添加结点,删除结点或改变结点的外观等更新并不会立即体现出来,这些操作将保存在一个队列中,待JavaScript引擎空闲时才有机会渲染出来.

GUI事件触发线程:

JavaScript脚本的执行不影响html元素事件的触发,在t1时间段内,首先是用户点击了一个鼠标键,点击被浏览器事件触发线程捕捉后形成一个鼠标点击事件,由图可知,对于JavaScript引擎线程来说,这事件是由其它线程异步传到任务队列尾的,由于引擎正在处理t1时的任务,这个鼠标点击事件正在等待处理.

定时触发线程:

注意这里的浏览器模型定时计数器并不是由JavaScript引擎计数的,因为JavaScript引擎是单线程的,如果处于阻塞线程状态就计不了时,它必须依赖外部来计时并触发定时,所以队列中的定时事件也是异步事件.

由图可知,在这t1的时间段内,继鼠标点击事件触发后,先前已设置的setTimeout定时也到达了,此刻对JavaScript引擎来说,定时触发线程产生了一个异步定时事件并放到任务队列中, 该事件被排到点击事件回调之后,等待处理.
同理, 还是在t1时间段内,接下来某个setInterval定时器也被添加了,由于是间隔定时,在t1段内连续被触发了两次,这两个事件被排到队尾等待处理.

可见,假如时间段t1非常长,远大于setInterval的定时间隔,那么定时触发线程就会源源不断的产生异步定时事件并放到任务队列尾而不管它们是否已被处理,但一旦t1和最先的定时事件前面的任务已处理完,这些排列中的定时事件就依次不间断的被执行,这是因为,对于JavaScript引擎来说,在处理队列中的各任务处理方式都是一样的,只是处理的次序不同而已.

t1过后,也就是说当前处理的任务已返回,JavaScript引擎会检查任务队列,发现当前队列非空,就取出t2下面对应的任务执行,其它时间依此类推,由此看来:

如果队列非空,引擎就从队列头取出一个任务,直到该任务处理完,即返回后引擎接着运行下一个任务,在任务没返回前队列中的其它任务是没法被执行的.

相信您现在已经很清楚JavaScript是否可多线程,也了解理解JavaScript定时器运行机制了,下面我们来对一些案例进行分析:
案例1:setTimeout与setInterval

setTimeout(function() {
        /* 代码块... */
        setTimeout(arguments.callee, 10);
}, 10);

setInterval(function(){
        /*代码块... */
}, 10);

这两段代码看一起效果一样,其实非也,第一段中回调函数内的setTimeout是JavaScript引擎执行后再设置新的setTimeout 定时, 假定上一个回调处理完到下一个回调开始处理为一个时间间隔,理论两个setTimeout回调执行时间间隔>=10ms .第二段自setInterval设置定时后,定时触发线程就会源源不断的每隔十秒产生异步定时事件并放到任务队列尾,理论上两个setInterval 回调执行时间间隔

案例2:ajax异步请求是否真的异步?

很多同学朋友搞不清楚,既然说JavaScript是单线程运行的,那么XMLHttpRequest在连接后是否真的异步?
其实请求确实是异步的,不过这请求是由浏览器新开一个线程请求(参见上图),当请求的状态变更时,如果先前已设置回调,这异步线程就产生状态变更事件放到 JavaScript引擎的处理队列中等待处理,当任务被处理时,JavaScript引擎始终是单线程运行回调函数,具体点即还是单线程运行 onreadystatechange所设置的函数.

分类: web前端 标签: ,

URL编码问题

2011年11月5日 没有评论

一般来说,URL只能使用英文字母、阿拉伯数字和某些标点符号,不能使用其他文字和符号。这意味着,如果URL中有汉字,就必须编码后使用。但是麻烦的是,RFC 1738没有规定具体的编码方法,而是交给应用程序(浏览器)自己决定。这导致“URL编码”成为了一个混乱的领域。

下面就让我们看看,“URL编码”到底有多混乱。我会依次分析四种不同的情况,在每一种情况中,浏览器的URL编码方法都不一样。

  • 情况1:网址路径中包含汉字

网址路径的编码,不管是IE还是Firefox都是用的是utf-8编码。

  • 情况2:查询字符串包含汉字

对于查询字符串的编码,需要依赖操作系统,用的是操作系统的默认编码。一般来说Window中文操作系统默认为GB2312编码,所以查询字符串的编码使用GB2312。

  • 情况3:Get方法生成的URL包含汉字

在已打开的网页上,当用用Get或Post方法发出HTTP请求,例如提交表单。这时编码方法由网页的编码决定,也就是由HTML源码中字符集的设定决定。

  • 情况4:Ajax调用的URL包含汉字

在Ajax调用中,IE和Firefox的处理方式完全不一样,IE总是采用GB2312编码(操作系统的默认编码),而Firefox总是采用utf-8编码。

面对如此多种情况,解决的办法是借助于javascript,通过js对url进行编码,然后再提交给服务器,不要给浏览器插手的机会。因为Javascript的输出总是一致的,所以就保证了服务器得到的数据是格式统一的。

javascript的编码函数可以使用encodeURI()或encodeURIComponent(),还有一个escape(),不提倡使用,对于encodeURI和encodeURIComponent输出的结果都是utf-8形式,并且在每个字节前加上%,所以服务器端就可以进行按utf-8统一解码,encodeURI和encodeURIComponent区别如下:。

encodeURI不编码字符有82个:!,#,$,&,’,(,),*,+,,,-,.,/,:,;,=,?,@,_,~,0-9,a-z,A-Z

encodeURIComponent不编码字符有71个:!, ‘,(,),*,-,.,_,~,0-9,a-z,A-Z

本文系转载,原文地址:http://www.ruanyifeng.com/blog/2010/02/url_encoding.html

分类: web前端 标签:

Google网站管理员工具左边菜单无法导航

2011年11月4日 没有评论

今天(2011-11-04)使用Google网站管理员工具时候,Firefox(版本3.6.13)下发现无法使用左边的菜单进行导航,并且左边有一红色的小方块,当鼠标点击某一个具体导航菜单时出现水平方向的滚动条覆盖在上面,导致无法点击,具体症状如下图:
查看大图
当在IE6下查看,页面布局惨不忍睹啊,难道google彻底放弃IE6了么,截图如下:

查看大图初步看了一下,造成水平滚动条的原因是在css文件4046579917-wmt.css中#navigation .item 设置的overflow:auto导致的。本想把这个问题提交Google,在帮助里找了一圈,没有发现能供接受Bug(应该可以算作吧)反馈的地址。好在safari下还能用,就凑合看看算了。

解决ssh空闲一段时间后自动断开的方法

2011年11月3日 没有评论

用客户端工具,例如securecrt连接linux服务器,有的会出现过一段时间没有任何操作,客户端与服务器就断开了连接。造成这个的原因,主要是因为客户端与服务器之间存在路由器,防火墙以及为了本身的安全性,在超过特定的时间后就会把空闲连接断开。或者是服务器端设置了断开空闲连接。那么解决的方法有两种,一是从服务器着手,一是在客户端工具上下手。

  • 服务器端

修改/etc/ssh/sshd_config配置文件 ClientAliveInterval 300(默认为0),参数的是意思是每5分钟,服务器向客户端发一个消息,用于保持连接,使用service sshd reload 让其修改后生效。如果发现还是有问题,可以试着把300设置小一点,例如60。

  • 客户端工具

securecrt的设置方法:会话选项 –> 终端 –> 反空闲–>选中发送协议 NO-OP(p) 每 60 秒

putty的设置方法:putty -> Connection -> Seconds between keepalives ( 0 to turn off ), 默认为0, 改为300

分类: Linux 标签: , ,

php-fpm进程管理方式

2011年11月1日 没有评论

目前最新5.3.x的php-fpm,有两种管理进程的方式,分别是static和dynamic。

如果设置成static,进程数自始至终都是pm.max_children指定的数量,pm.start_servers,pm.min_spare_servers,pm.max_spare_servers配置将没有作用。

如果设置成dynamic,则进程数是动态的,最开始是pm.start_servers指定的数量,如果请求较多,则会自动增加,但不超过pm.max_children指定的数量,同时保证空闲的进程数不小于pm.min_spare_servers,如果进程数较多,也会进行相应清理,保证多余的进程数不多于pm.max_spare_servers。

当php-fpm启动后,一个php-cgi进程约战3M内存,但是当它们处理过一些请求后,有些内存是释放不掉的,占用的内存能达到20M-30M不等。

对于内存比较吃紧,同时并发量不是很大的应用,可以考虑采用static的方式,这样可以很好的控制php-fpm的所消耗的总内存数,让系统更加平稳运行。另外由于并发量很小,可以适当的把设置pm.max_requests小一些,以便让php-fpm进程有机会重启,从而释放其占用的内存。

nginx+tomcat配置负载均衡

2011年10月31日 没有评论

使用nginx做为前端服务器,tomcat作为后端服务器,通过一些简单的配置就可以达到tomcat集群的目的。一般情况下,只需要在nginx的配置文件nginx.conf进行如下配置即可。

http {
    upstream  tomcat-host{
        server 192.168.1.201:8080 weight=3;
        server 192.168.1.202:8080;
        ip_hash;
    }  

    server {
        listen 80;
        server_name www.domain.com;  

        location / {
            proxy_pass              http://tomcat-host;
            proxy_set_header        X-Real-IP $remote_addr;
            proxy_set_header        X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header        Host $http_host;
        }
    }
}

其中upstream是配置后端服务器列表、load balance的权重,ip_hash能够把来自同一个客户端的多次请求指派到某个固定的后端服务器,能一定程度的解决session问题,如果完美的解决集群后session问题,可以采用memcached的方式来处理。在server段配置中,主要就是proxy_pass,prxoy_pass目标地址就是上述设置的upstream名字,注意,不能缺少“http://”,proxy_set_header处理客户端的IP信息,由于前端是nginx,在tomcat的java应用中用request.getRemoteAddr(),得到的ip信息不再是客户端的ip,而是nginx服务器的ip,解决办法是需要修改java应用,大致可以使用如下代码:

String ip = request.getHeader("x-forwarded-for");

if (ip == null || "".equals(ip.trim())) {
	ip = request.getRemoteAddr();
} else {
	String[] ars = ip.split(",");
	ip = ars[ars.length-1].trim();
}

http状态码204理解

2011年10月31日 没有评论

HTTP的状态码有很多种,主要有1xx(临时响应)、2xx(成功)、3xx(已重定向)、4xx(请求错误)以及5xx(服务器错误)五个大类,每个大类还对应一些具体的分类。平时我们接触比较多的是200、400、500等。

这里我们主要讨论一下状态码204,在HTTP RFC 2616中关于204的描述如下:

If the client is a user agent, it SHOULD NOT change its document view from that which caused the request to be sent. This response is primarily intended to allow input for actions to take place without causing a change to the user agent’s active document view, although any new or updated metainformation SHOULD be applied to the document currently in the user agent’s active view.

意思等同于请求执行成功,但是没有数据,浏览器不用刷新页面.也不用导向新的页面。如何理解这段话呢。还是通过例子来说明吧,假设页面上有个form,提交的url为http-204.htm,提交form,正常情况下,页面会跳转到http-204.htm,但是如果http-204.htm的相应的状态码是204,此时页面就不会发生转跳,还是停留在当前页面。另外对于a标签,如果链接的页面响应码为204,页面也不会发生跳转。

所以对于一些提交到服务器处理的数据,只需要返回是否成功的情况下,可以考虑使用状态码204来作为返回信息,从而省掉多余的数据传输。

分类: web前端 标签: , ,

更改Centos的yum源

2011年10月31日 没有评论

前段时间一直使用http://centos.ustc.edu.cn/的源,最近发现更新有问题,遂换成默认的源,执行yum update的时候还是报错,似乎原因是还在使用默认的ustc的源,后来查了一下,把/etc/yum.repos.d目录下的文件全部干掉,即 rm -rf /etc/yum.repos.d/*就可以了。在国内163的源似乎也不错,添加方法为:

cd /etc/yum.repos.d/
wget http://mirrors.163.com/.help/CentOS-Base-163.repo
mv CentOS-Base-163.repo CentOS-Base.repo
yum makecache
yum update
分类: Linux 标签: ,

nginx设置wordpress静态资源压缩和过期时间设置

2011年10月27日 没有评论

通过对一些静态资源简单的设置,例如设置静态资源的过期时间、以及压缩,可以减少网站的加载时间,同时也能减少服务器的带宽。

在nginx下对静态的过期时间设置为:

        location ~* \.(ico|gif|bmp|jpg|jpeg|png|swf|js|css) {
            root    /var/www/netingcn.com;
            expires 7d;
        }

上述配置能对图片、flash、js、css文件设置了过期时间为7天,当用户在7天内再次访问网站时,大部分情况下都是直接使用本地的缓存,减少网络传输,从而加快了页面加载速度。

压缩的配置如下:

        gzip  on;
        gzip_min_length  1000;
        gzip_buffers     4 8k;
        gzip_types       text/plain application/x-javascript text/css;

对文本、js和css文件进行压缩,一般情况下,压缩后的大小是原始大小的25%,甚至更小。

centos、utunbu安装软件

2011年10月26日 没有评论

centos下配置好软件源,可以通过yum方便的安装需要的软件,例如:yum install vim,注意执行这样的操作需要root权限,切换到root下,或者用sudo yum install vim。不过有时候软件的名字和包名并不是一致的,在某个版本后centos就没有包含whois命令,当使用 yum install whois提示No package whois available,想要知道whois对于的包名,可以借助于yum search whois,执行后发现有jwhois.i686 : Internet whois/nicname client这行信息,然后使用yum install jwhois,顺利安装上whois了。

另外,在linux的另外一个发行版Ubuntu中,是采用apt来管理软件包的,对于的安装命令为apt-get install xxx,如果要搜索软件对应的包名,则是使用apt-cache search xxx

无觅相关文章插件,快速提升流量