<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>西红柿爱番茄 &#187; Ajax</title>
	<atom:link href="http://www.ilovejs.net/archives/category/ajax/feed" rel="self" type="application/rss+xml" />
	<link>http://www.ilovejs.net</link>
	<description>到了创造为主的阶段，不忘继续学习</description>
	<lastBuildDate>Thu, 15 Dec 2011 06:18:39 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.3</generator>
		<item>
		<title>Fetch resources on-demand</title>
		<link>http://www.ilovejs.net/archives/1258</link>
		<comments>http://www.ilovejs.net/archives/1258#comments</comments>
		<pubDate>Mon, 11 Oct 2010 17:01:17 +0000</pubDate>
		<dc:creator>Supersha</dc:creator>
				<category><![CDATA[Ajax]]></category>
		<category><![CDATA[Javascript]]></category>
		<category><![CDATA[性能优化]]></category>
		<category><![CDATA[按需加载]]></category>

		<guid isPermaLink="false">http://www.ilovejs.net/?p=1258</guid>
		<description><![CDATA[Fetch resources on-demand，中文意思大概就是按需加载资源，而这里的资源不只是CSS、JS文件，Image、swf、Content等都可以利用这种方式。特别是当前Yahoo的首页：http://www.yahoo.com，几乎页面的每一个模块都体现了按需加载的技巧：左边的导航（用户可以定制、点击异步请求更多资源）、中间的新闻、图片tab、右栏的第三方资源等等。 按需加载表面理解起来很简单，但是怎么个按需法呢？我所理解的按需，包括两个方面：一个是按用户的需要；另一个是网站本身的需要。按需加载主要解决的问题就是加快页面的初始化以及在页面初始化的时候最大化减少HTTP请求，因为有些模块比如：图片导航tab、多级菜单、内容导航等等，这些在初始化页面的时候有很多内容都是处于隐藏状态，用户是看不到的，那么这些内容对不同的用户来说有不同的需求，那么这些就可以从页面的初始化中剥离出来，按用户的需求通过异步通信来加载内容和资源。对于网站本身的需要来加载后续资源，比如Google首页对于结果页的一些js、image等资源的pre-fetch的方式，通过分析用户可能会产生的行为，为后续的页面提前加载内容，加快后续页面的初始化速度，从缓存中读取页面所需要的资源。 接下来分析一下淘宝首页（http://www.taobao.com）的一些按需加载的技巧。首先对于中间栏的这个部分的实现： 看这个模块的源码之后发现，除了初始化第一个tab内容之后，后面隐藏的几个tab的资源是没有加载的，它是将这些资源的HTML设置到一个隐藏的textarea控件的值里面，在点击了左右的导航按钮之后，就读出这个textarea的内容并解析到tab中，当然这也同时是加载图片等资源。我想，这样做的一个好处就是上面说的按需加载，加快页面的初始化速度；还有另外一个作用是减少页面初始化的DOM节点数。将这个添加更多DOM节点推迟到按照用户的需要去点击导航按钮。这个在加快页面的初始化方面也是很不错的技巧。 再来分析一下Yahoo首页的其中一个模块的实现方式，也是比较类似的： 如上图，在下部的导航中，初始化的时候会默认加载一个tab看到的导航的图片和问题，而其他隐藏的每一个导航tab，在页面初始化的时候HTML的结构均使用四个span标签代替，之后在点击左右导航按钮的时候，进行异步请求资源，并替换掉每一个相应的span标签。它在初始化页面的时候使用span标签，只是为了保持一个高度上的“空架子”，在之后的按需请求中往这个架子中替换内容，这样其中的一个原因可能是为了不至于在替换标签的时候在高度上的过度出现闪烁。 按需加载的思维在很多网站中都有体现，利用Ajax技术，异步请求内容，将隐藏的内容、资源的加载跟页面的初始化剥离开来，加快页面的初始化。]]></description>
			<content:encoded><![CDATA[<p>
Fetch resources on-demand，中文意思大概就是按需加载资源，而这里的资源不只是CSS、JS文件，Image、swf、Content等都可以利用这种方式。特别是当前Yahoo的首页：<a  href="http://www.yahoo.com">http://www.yahoo.com</a>，几乎页面的每一个模块都体现了按需加载的技巧：左边的导航（用户可以定制、点击异步请求更多资源）、中间的新闻、图片tab、右栏的第三方资源等等。
</p>
<p>
按需加载表面理解起来很简单，但是怎么个按需法呢？我所理解的按需，包括两个方面：一个是按用户的需要；另一个是网站本身的需要。按需加载主要解决的问题就是加快页面的初始化以及在页面初始化的时候最大化减少HTTP请求，因为有些模块比如：图片导航tab、多级菜单、内容导航等等，这些在初始化页面的时候有很多内容都是处于隐藏状态，用户是看不到的，那么这些内容对不同的用户来说有不同的需求，那么这些就可以从页面的初始化中剥离出来，按用户的需求通过异步通信来加载内容和资源。对于网站本身的需要来加载后续资源，比如Google首页对于结果页的一些js、image等资源的pre-fetch的方式，通过分析用户可能会产生的行为，为后续的页面提前加载内容，加快后续页面的初始化速度，从缓存中读取页面所需要的资源。
</p>
<p><span id="more-1258"></span></p>
<p>
接下来分析一下淘宝首页（<a  href="http://www.taobao.com">http://www.taobao.com</a>）的一些按需加载的技巧。首先对于中间栏的这个部分的实现：
</p>
<p>
<img src="http://www.ilovejs.net/wp-content/uploads/2010/10/taobao.jpg" alt="" title="taobao" width="478" height="143" class="alignnone size-full wp-image-1259" />
</p>
<p>
看这个模块的源码之后发现，除了初始化第一个tab内容之后，后面隐藏的几个tab的资源是没有加载的，它是将这些资源的HTML设置到一个隐藏的textarea控件的值里面，在点击了左右的导航按钮之后，就读出这个textarea的内容并解析到tab中，当然这也同时是加载图片等资源。我想，这样做的一个好处就是上面说的按需加载，加快页面的初始化速度；还有另外一个作用是减少页面初始化的DOM节点数。将这个添加更多DOM节点推迟到按照用户的需要去点击导航按钮。这个在加快页面的初始化方面也是很不错的技巧。
</p>
<p>
再来分析一下Yahoo首页的其中一个模块的实现方式，也是比较类似的：
</p>
<p>
<img src="http://www.ilovejs.net/wp-content/uploads/2010/10/yahoo1.jpg" alt="" title="yahoo" width="403" height="440" class="alignnone size-full wp-image-1261" />
</p>
<p>
如上图，在下部的导航中，初始化的时候会默认加载一个tab看到的导航的图片和问题，而其他隐藏的每一个导航tab，在页面初始化的时候HTML的结构均使用四个span标签代替，之后在点击左右导航按钮的时候，进行异步请求资源，并替换掉每一个相应的span标签。它在初始化页面的时候使用span标签，只是为了保持一个高度上的“空架子”，在之后的按需请求中往这个架子中替换内容，这样其中的一个原因可能是为了不至于在替换标签的时候在高度上的过度出现闪烁。
</p>
<p>
按需加载的思维在很多网站中都有体现，利用Ajax技术，异步请求内容，将隐藏的内容、资源的加载跟页面的初始化剥离开来，加快页面的初始化。</p>
]]></content:encoded>
			<wfw:commentRss>http://www.ilovejs.net/archives/1258/feed</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>并行加载、按顺序执行javascript文件</title>
		<link>http://www.ilovejs.net/archives/976</link>
		<comments>http://www.ilovejs.net/archives/976#comments</comments>
		<pubDate>Mon, 21 Jun 2010 04:27:11 +0000</pubDate>
		<dc:creator>Supersha</dc:creator>
				<category><![CDATA[Ajax]]></category>
		<category><![CDATA[Javascript]]></category>
		<category><![CDATA[lazy definition]]></category>
		<category><![CDATA[并行加载]]></category>
		<category><![CDATA[性能优化]]></category>

		<guid isPermaLink="false">http://www.ilovejs.net/?p=976</guid>
		<description><![CDATA[之前一直在想法设法的实现js脚本文件按顺序执行，而且并行下载的测试，但是都无终而返。最先设想的是通过轮询的方式来实现按顺序加载，可是这样就导致不能并行加载了。 今天又拾起这个一直未实现的冲动，首先既然要并行加载、按顺序执行，那么就必须得给每一个js文件开一个http请求，而且这个请求是无顺序的，即是加载的顺序是无顺序的，但是在执行的时候，就得要按照声明的顺序来执行，比如下面的代码示例： [javascript] jL.load({&#34;label&#34;:&#34;test&#34;,&#34;url&#34;:&#34;test.js&#34;,isOrder:true}); jL.load({&#34;label&#34;:&#34;test2&#34;,&#34;url&#34;:&#34;test2.js&#34;,isOrder:false}); jL.load({&#34;label&#34;:&#34;test3&#34;,&#34;url&#34;:&#34;test3.js&#34;,isOrder:true,callback:function(){ alert(&#34;test3.js is load&#34;); }}).insert(); [/javascript] 上面的代码中，load方法的参数中包含是否按顺序加载脚本文件，并且在脚本加载下来的时候包含callback函数回调。如果不是按顺序下载的，则直接使用DOM Element的方式插入script到document.body中；insert方法负责开始操作那些俺顺序执行的脚本文件的加载和插入工作；label参数对于需要按顺序执行的脚本文件必不可少，用于标识当前脚本，对无需按顺序执行的脚本可有可无。 刚开始设想的原理是这样的：把需要按顺序执行的脚本通过load方法都临时储存到一个数组里（如果不需要，则直接在load方法里通过DOM Element方法插入），之后调用insert方法来执行上面临时数组（使用XHR Injection的方式获得每一个脚本的responseText），并将请求返回的脚本的内容也暂且储存在临时变量里，待全部的脚本都请求下来之后，再把全部脚本的内容都插入到Document文档里使得脚本起作用。《测试示例1》 上面的方案虽然实现了并行加载、按顺序执行，但是存在诸多弊端，如果有脚本文件比较大，请求的时间比较长，那么先于它加载下来的脚本就迟迟得不到执行了，而且在处理callback回调函数的时候也是在全部脚本加载下来以后才执行。 为此，在这个基础上，修改了实现方式：按顺序执行的脚本仍然使用XHR的方式来加载，并把内容临时储存在数组里，同时通过label标识着脚本文件添加一个load的boolean属性来标识该脚本是否已经加载完成。这样，我就可以使用定时器(setInterval)来检查按label标识顺序排好的数组来检查load属性是否为true，这样来插入脚本到文档中。这样，就可以尽早的将声明在前面的脚本在加载完成的时候就插入到文档内。《测试页面》 在代码中，因为在重复调用insert方法的情况下要处理临时数组和对象的清空问题比较难解决，所以就进行了再一次的封装，支持链式调用，而且总共也才只有load和insert方法，参数说明：label用于标识该脚本文件以示区分，对于isOrder为true时labe必不可少，isOrder为false时可有可无；callback是回调函数，url为脚本的地址。操作方式如下几种： [javascript] &#8230; <a href="http://www.ilovejs.net/archives/976" class="more-link">了解更多</a>]]></description>
			<content:encoded><![CDATA[<p>
之前一直在想法设法的实现js脚本文件按顺序执行，而且并行下载的测试，但是都无终而返。最先设想的是通过轮询的方式来实现按顺序加载，可是这样就导致不能并行加载了。
</p>
<p>今天又拾起这个一直未实现的冲动，首先既然要并行加载、按顺序执行，那么就必须得给每一个js文件开一个http请求，而且这个请求是无顺序的，即是加载的顺序是无顺序的，但是在执行的时候，就得要按照声明的顺序来执行，比如下面的代码示例：<br />
[javascript]<br />
jL.load({&quot;label&quot;:&quot;test&quot;,&quot;url&quot;:&quot;test.js&quot;,isOrder:true});<br />
jL.load({&quot;label&quot;:&quot;test2&quot;,&quot;url&quot;:&quot;test2.js&quot;,isOrder:false});<br />
jL.load({&quot;label&quot;:&quot;test3&quot;,&quot;url&quot;:&quot;test3.js&quot;,isOrder:true,callback:function(){<br />
  alert(&quot;test3.js is load&quot;);<br />
}}).insert();<br />
[/javascript]</p>
<p>
上面的代码中，load方法的参数中包含是否按顺序加载脚本文件，并且在脚本加载下来的时候包含callback函数回调。如果不是按顺序下载的，则直接使用DOM Element的方式插入script到document.body中；insert方法负责开始操作那些俺顺序执行的脚本文件的加载和插入工作；label参数对于需要按顺序执行的脚本文件必不可少，用于标识当前脚本，对无需按顺序执行的脚本可有可无。
</p>
<p><span id="more-976"></span></p>
<p>
刚开始设想的原理是这样的：把需要按顺序执行的脚本通过load方法都临时储存到一个数组里（如果不需要，则直接在load方法里通过DOM Element方法插入），之后调用insert方法来执行上面临时数组（使用XHR Injection的方式获得每一个脚本的responseText），并将请求返回的脚本的内容也暂且储存在临时变量里，待全部的脚本都请求下来之后，再把全部脚本的内容都插入到Document文档里使得脚本起作用。《<a  href="/lab/load-order/">测试示例1</a>》
</p>
<p>
上面的方案虽然实现了并行加载、按顺序执行，但是存在诸多弊端，如果有脚本文件比较大，请求的时间比较长，那么先于它加载下来的脚本就迟迟得不到执行了，而且在处理callback回调函数的时候也是在全部脚本加载下来以后才执行。
</p>
<p>
为此，在这个基础上，修改了实现方式：按顺序执行的脚本仍然使用XHR的方式来加载，并把内容临时储存在数组里，同时通过label标识着脚本文件添加一个load的boolean属性来标识该脚本是否已经加载完成。这样，我就可以使用定时器(setInterval)来检查按label标识顺序排好的数组来检查load属性是否为true，这样来插入脚本到文档中。这样，就可以尽早的将声明在前面的脚本在加载完成的时候就插入到文档内。《<a  href="/lab/load-order/index3.html">测试页面</a>》
</p>
<p>
在代码中，因为在重复调用insert方法的情况下要处理临时数组和对象的清空问题比较难解决，所以就进行了再一次的封装，支持链式调用，而且总共也才只有load和insert方法，参数说明：label用于标识该脚本文件以示区分，对于isOrder为true时labe必不可少，isOrder为false时可有可无；callback是回调函数，url为脚本的地址。操作方式如下几种：
</p>
<p>[javascript]<br />
//分类的方式，几个insert方法调用<br />
jL().load({&quot;label&quot;:&quot;jquery&quot;,&quot;url&quot;:&quot;jquery.js&quot;,isOrder:true,callback:function(){<br />
  alert($(&quot;#test&quot;).html());<br />
}})<br />
.load({&quot;label&quot;:&quot;test&quot;,&quot;url&quot;:&quot;test.js&quot;,isOrder:true})<br />
.insert();<br />
///////////////////////////////////<br />
jL().load({&quot;label&quot;:&quot;test2&quot;,&quot;url&quot;:&quot;test2.js&quot;,isOrder:true})<br />
.load({&quot;label&quot;:&quot;test3&quot;,&quot;url&quot;:&quot;test3.js&quot;,isOrder:true})<br />
.insert();<br />
//链式调用的方式，最后一个insert方法调用：<br />
jL().load({&quot;label&quot;:&quot;jquery&quot;,&quot;url&quot;:&quot;jquery.js&quot;,isOrder:false,callback:function(){<br />
  alert($(&quot;#test&quot;).html());<br />
}})<br />
.load({&quot;label&quot;:&quot;test&quot;,&quot;url&quot;:&quot;test.js&quot;,isOrder:true})<br />
.load({&quot;label&quot;:&quot;test2&quot;,&quot;url&quot;:&quot;test2.js&quot;,isOrder:true})<br />
.load({&quot;label&quot;:&quot;test3&quot;,&quot;url&quot;:&quot;test3.js&quot;,isOrder:true}).insert();<br />
//可以将加载信息都声明到一个load方法里：<br />
jL().load(<br />
{&quot;label&quot;:&quot;jquery&quot;,&quot;url&quot;:&quot;jquery.js&quot;,isOrder:true,callback:function(){<br />
  alert($(&quot;#test&quot;).html());<br />
}},<br />
{&quot;label&quot;:&quot;test&quot;,&quot;url&quot;:&quot;test.js&quot;,isOrder:true},<br />
{&quot;label&quot;:&quot;test2&quot;,&quot;url&quot;:&quot;test2.js&quot;,isOrder:true},<br />
{&quot;label&quot;:&quot;test3&quot;,&quot;url&quot;:&quot;test3.js&quot;,isOrder:true}<br />
)<br />
.insert();<br />
[/javascript]</p>
<p>
如上所示，操作简单方便而且实用，下面是js源码：
</p>
<p>[javascript]<br />
; (function(win) {<br />
  var _h = document.getElementsByTagName(&quot;head&quot;)[0];<br />
  //使用lazy Definition的方式声明xhr实例<br />
  var _xhr = function() {<br />
    _xhr = win.XMLHttpRequest ? function() {<br />
      return new XMLHttpRequest();<br />
    }: (new ActiveXObject(&quot;Msxml2.XMLHTTP&quot;)) ? function() {<br />
      return new ActiveXObject(&quot;Msxml2.XMLHTTP&quot;);<br />
    }: function() {<br />
      return new ActiveXObject(&quot;Microsoft.XMLHTTP&quot;);<br />
    }<br />
    return _xhr();<br />
  }<br />
  //使用DOM Element的方式把脚本动态插入到文档?<br />
  var _domLoad = function(url, parent, callback) {<br />
    if (!url){<br />
       return;<br />
    }<br />
    var s = document.createElement(&quot;script&quot;);<br />
    s.setAttribute(&quot;src&quot;, url); (parent || _h).appendChild(s);<br />
    if (callback) {<br />
      if (s.readyState) { //IE,Opera<br />
        s.onreadystatechange = function() {<br />
          if (s.readyState === &quot;loaded&quot; || s.readyState === &quot;complete&quot;) {<br />
            s.onreadystatechange = null;<br />
            callback();<br />
          }<br />
        }<br />
      } else { // FF,Chrome,Safari<br />
        s.onload = function() {<br />
          s.onload = null;<br />
          callback();<br />
        }<br />
      }<br />
    }<br />
  }<br />
  //通过设置script标签的text属性来插入行内脚本到文档中<br />
  //不过一定要插入到head中，插入到body中在IE6下测试出?<br />
  var _script = function(text) {<br />
    var s = document.createElement(&quot;script&quot;);<br />
    s.text = text;<br />
    _h.appendChild(s);<br />
  }<br />
  //请求每一个js脚本文件<br />
  var _request = function(_p, _o) {<br />
    var _x = _xhr(),<br />
    item = _o._cache[_p.label];<br />
    _x.open(&quot;GET&quot;, _p.url, true);<br />
    _x.onreadystatechange = function() {<br />
      if (_x.readyState === 4 &amp;&amp; (_x.status === 200 || _x.status === 304)) {<br />
        item[&quot;text&quot;] = _x.responseText;<br />
        item[&quot;load&quot;] = true;<br />
      }<br />
    }<br />
    _x.send(null);<br />
  }<br />
  var _load =function (){<br />
	for(var i=0,l=arguments.length;i&lt;l;i++){<br />
	  var a = arguments[i];<br />
	  if(!a.isOrder){<br />
	    _domLoad(a.url,a.parent,a.callback);<br />
	  }else{<br />
	    this._param.push(a);<br />
	    this._cache[a.label]={};<br />
	    this._cache[a.label][&quot;callback&quot;]=a.callback;<br />
	  }<br />
	}<br />
	return this;<br />
  }<br />
  var _insert = function() {<br />
    if (!this._param.length) {<br />
      return;<br />
    }<br />
    var _o = null,i = 0;<br />
	//请求脚本文件<br />
    while (_o = this._param[i++]) {<br />
      _request(_o, this);<br />
    }<br />
	//定时器检查并插入脚本到文档中<br />
    var o = this._param.shift(),item = null,that = this;<br />
    var interval = setInterval(function() {<br />
      item = that._cache[o.label];<br />
      if (item[&quot;load&quot;]) {<br />
        _script(item[&quot;text&quot;]);<br />
        if (item[&quot;callback&quot;]) {<br />
          item[&quot;callback&quot;]();<br />
        }<br />
        if (! (o = that._param.shift())) {<br />
          clearInterval(interval);<br />
        }<br />
      }<br />
    },5);<br />
    return this;<br />
  }<br />
  var _jL = function() {<br />
    this._cache = {};<br />
    this._param = [];<br />
  }<br />
  _jL.prototype = {<br />
    load: _load,<br />
    insert: _insert<br />
  }<br />
  win.jL = function() {<br />
    return new _jL();<br />
  }<br />
})(this);<br />
[/javascript]</p>
]]></content:encoded>
			<wfw:commentRss>http://www.ilovejs.net/archives/976/feed</wfw:commentRss>
		<slash:comments>11</slash:comments>
		</item>
		<item>
		<title>对长轮询Ajax调用提出的疑点</title>
		<link>http://www.ilovejs.net/archives/269</link>
		<comments>http://www.ilovejs.net/archives/269#comments</comments>
		<pubDate>Sat, 06 Feb 2010 07:10:22 +0000</pubDate>
		<dc:creator>Supersha</dc:creator>
				<category><![CDATA[Ajax]]></category>
		<category><![CDATA[Javascript]]></category>

		<guid isPermaLink="false">http://www.ilovejs.net/?p=269</guid>
		<description><![CDATA[//////////////////////// 2010-02-19 update ///////////////////////////// 对于实现这个轮询发出请求来更新页面UI的功能，还可以通过setInterval定时器来实现，不过普通的定时器的实现方式是在规定的时间内重复的发出请求，而不管前面的请求是否已经完成，这样的实现方式是有很多弊端的。从这点出发，我修改了通过定时器setInterval的实现方式，通过一个布尔值来判断前面的请求是否完成，没完成的话就压缩到堆栈里等待执行。下面是实现的源码： [javascript] /* * @author Shanpeng * @website:http://www.ilovejs.net * @e-mail:supersha@foxmail/shanpeng@taobao.com/supershafeng@gmail.com * @QQ:770104121 * @my word:Here,believe what &#8230; <a href="http://www.ilovejs.net/archives/269" class="more-link">了解更多</a>]]></description>
			<content:encoded><![CDATA[<p>
//////////////////////// <em>2010-02-19 update</em> /////////////////////////////
</p>
<p>对于实现这个轮询发出请求来更新页面UI的功能，还可以通过setInterval定时器来实现，不过普通的定时器的实现方式是在规定的时间内重复的发出请求，而不管前面的请求是否已经完成，这样的实现方式是有很多弊端的。从这点出发，我修改了通过定时器setInterval的实现方式，通过一个布尔值来判断前面的请求是否完成，没完成的话就压缩到堆栈里等待执行。下面是实现的源码：<br />
[javascript]<br />
/*<br />
 * @author Shanpeng<br />
 * @website:http://www.ilovejs.net<br />
 * @e-mail:supersha@foxmail/shanpeng@taobao.com/supershafeng@gmail.com<br />
 * @QQ:770104121<br />
 * @my word:Here,believe what browsers display,and believe yourself<br />
 */<br />
////////////////////// Javascript&#8217;s code below ///////////////////////<br />
(function(global){<br />
    var XHR = function(){<br />
        var xhr = false;<br />
        if (window.XMLHttpRequest) {<br />
            xhr = new XMLHttpRequest();<br />
        }<br />
        else if (window.ActiveXObject(&quot;Msxml2.XMLHTTP&quot;)) {<br />
            xhr = new ActiveXObject(&quot;Msxml2.XMLHTTP&quot;);<br />
        }<br />
        else {<br />
            xhr = new ActiveXObject(&quot;Microsoft.XMLHTTP&quot;);<br />
        }<br />
        return xhr;<br />
    }</p>
<p>    var query = [];<br />
    var xhr = XHR();<br />
    var ready = true;  //用于判断某个请求响应是否成功</p>
<p>    var queryAjax = function(url, callback){<br />
        query[query.length] = [url, callback];<br />
        if (ready) {<br />
            var cur = query.shift();<br />
            xhr.open(&quot;GET&quot;, cur[0], true);<br />
            xhr.onreadystatechange = function(){<br />
                if (xhr.readyState === 4 &amp;&amp; (xhr.status === 200 || xhr.status === 304)) {<br />
                    cur[1](xhr);<br />
                    ready = true;<br />
                }<br />
            }<br />
            xhr.send(null);<br />
        }<br />
        ready = false;<br />
    }<br />
    global.queryAjax = queryAjax;<br />
})(this);</p>
<p>//Usage：<br />
onload = function(){<br />
    var div = document.getElementById(&quot;test&quot;);<br />
    var i = 0;<br />
    setInterval(function(){<br />
        queryAjax(&quot;test.txt&quot;, function(obj){<br />
            div.innerHTML = obj.responseText + (++i);<br />
        });<br />
    }, 1000);<br />
}<br />
[/javascript]</p>
<p>
///////////////////////////////////////////////////////////////////
</p>
<p>前几天接触到关于长轮询调用Ajax的技巧，原理很简单：就是在前一次Ajax调用并且响应成功之后再调用XHR对象的open和send方法，继续发送请求。代码示例如下：<br />
[javascript]<br />
var sendRequest = function(url, callback){<br />
    var xhr = XHR();<br />
    if (typeof xhr === &quot;string&quot;) throw xhr;<br />
    xhr.open(&quot;GET&quot;, url, true);<br />
    xhr.onreadystatechange = function(){<br />
        if (xhr.readyState === 4 &amp;&amp; xhr.status === 200) {<br />
            //调用回调函数处理响应<br />
            callback(xhr);<br />
            //重新打开连接<br />
            xhr.open(&quot;GET&quot;, url, true);<br />
            xhr.send(null);<br />
        }<br />
    }<br />
    xhr.send(null);<br />
}<br />
[/javascript]<br />
长轮询的关键点就在onreadystatechange事件内，当响应成功之后再打开连接，发送请求。这个比起使用定时器的方式又优化很多了。定时器的代码示例如下：<br />
[javascript]<br />
var sendRequest=function(url,callback){<br />
    var xhr=XHR();<br />
    if(typeof xhr === &quot;string&quot;) throw xhr;<br />
    xhr.open(&quot;GET&quot;,url,true);<br />
    xhr.onreadystatechange=function(){<br />
         if(xhr.readyState === 4 &amp;&amp; xhr.status === 200){<br />
              callback(xhr);<br />
         }<br />
    }<br />
    xhr.send(null):<br />
}<br />
//设置定时器<br />
setInterval(sendRequest,1000);<br />
[/javascript]</p>
<p>定时器技巧可能会引发一个致命的问题，就是在时间间隔内如果响应还没有完成，下一个请求就又发出了，这将会导致过多的无用请求，同时也会拖慢系统的运行。
</p>
<p>
但是这篇文章并不是来说明定时器技巧和长轮询的优异，而是为了说明我在测试长轮询的过程中发现的一个问题。就拿上面第一段展示长轮询的代码来说，如果在IE8下，将会导致崩溃；Google Chrome、Safari、Opera和Firefox下正常。《<a href="http://www.ilovejs.net/lab/longLoop.html">测试页面</a>》
</p>
<p>
我通过将定时器跟长轮询结合起来，就可以避免这个问题，在各个浏览器下测试正常，代码示例如下：
</p>
<p>[javascript]<br />
var sendRequest = function(url, callback){<br />
    var xhr = XHR();<br />
    if (typeof xhr === &quot;string&quot;)<br />
        throw xhr;<br />
    xhr.open(&quot;GET&quot;, url, true);<br />
    xhr.onreadystatechange = function(){<br />
        if (xhr.readyState === 4) {<br />
            callback(xhr);<br />
            //设置一个0时间间隔的定时器<br />
            setTimeout(function(){<br />
                xhr.open(&quot;GET&quot;, url, true);<br />
                xhr.send(null);<br />
            }, 0);<br />
        }<br />
    }<br />
    xhr.send(null);<br />
}<br />
[/javascript]</p>
<p>
在上面的代码示例中，在onreadystatechange事件中在响应成功之后设置一个0时间间隔的定时器，这就解决了IE下的问题。《<a href="http://www.ilovejs.net/lab/longLoop-setTimeout.html">测试页面</a>》，原理可能是定时器本身需要一个生成并执行的过程，这个就给了javascript解析器一个缓冲的过程，从而避免了“死循环”使得javascript解析器错误的解析判断代码。
</p>
<p>在IE下崩溃的主要原因一方面是“死循环”，但是主要还是回调函数比较简单，执行的时间非常的短，使得IE的JScript解析器执行的次数超过了极限。或许回调函数如果能执行比较长时间的话，IE的这个问题也就无需使用0定时器了。</p>
]]></content:encoded>
			<wfw:commentRss>http://www.ilovejs.net/archives/269/feed</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>实现Ajax请求队列按顺序执行</title>
		<link>http://www.ilovejs.net/archives/163</link>
		<comments>http://www.ilovejs.net/archives/163#comments</comments>
		<pubDate>Wed, 06 Jan 2010 08:45:40 +0000</pubDate>
		<dc:creator>Supersha</dc:creator>
				<category><![CDATA[Ajax]]></category>
		<category><![CDATA[Javascript]]></category>

		<guid isPermaLink="false">http://www.ilovejs.net/?p=163</guid>
		<description><![CDATA[之前想做一个Web桌面的项目，考虑的必须得使用Ajax请求队列，使得Ajax的请求能按照队列按顺序执行，解决了Ajax异步传输覆盖的问题，也看了几个别人的代码，都整不太明白，所以自己干脆自己搞搞阵，自己实现，也得个安慰奖。我的实现方法很简单，通过递归调用函数，而无需使用定时器去检查Ajax请求是否已经执行完毕，具体的例子请看下面的代码： Javascript: [javascript] onload = function(){ document.getElementById(&#34;btn&#34;).onclick = function(){ //添加请求队列 addAjax({method: &#34;GET&#34;,url: &#34;test2.txt&#34;,callback: callback1}); addAjax({method: &#34;GET&#34;,url: &#34;test.txt&#34;,callback: callback2}); addAjax({method: &#34;GET&#34;,url: &#8230; <a href="http://www.ilovejs.net/archives/163" class="more-link">了解更多</a>]]></description>
			<content:encoded><![CDATA[<p>
之前想做一个Web桌面的项目，考虑的必须得使用Ajax请求队列，使得Ajax的请求能按照队列按顺序执行，解决了Ajax异步传输覆盖的问题，也看了几个别人的代码，都整不太明白，所以自己干脆自己搞搞阵，自己实现，也得个安慰奖。我的实现方法很简单，通过递归调用函数，而无需使用定时器去检查Ajax请求是否已经执行完毕，具体的例子请看下面的代码：
</p>
<p>Javascript:</p>
<p>[javascript]<br />
onload = function(){<br />
    document.getElementById(&quot;btn&quot;).onclick = function(){<br />
        //添加请求队列<br />
        addAjax({method: &quot;GET&quot;,url: &quot;test2.txt&quot;,callback: callback1});<br />
        addAjax({method: &quot;GET&quot;,url: &quot;test.txt&quot;,callback: callback2});<br />
        addAjax({method: &quot;GET&quot;,url: &quot;test2.txt&quot;,callback: callback3});<br />
        //开始执行队列<br />
        executeAjax();<br />
    }<br />
}</p>
<p>var callback1 = function(data, xhr){<br />
    document.getElementById(&quot;div1&quot;).innerHTML = data;<br />
}<br />
var callback2 = function(data, xhr){<br />
    document.getElementById(&quot;div2&quot;).innerHTML = data;<br />
}<br />
var callback3 = function(data, xhr){<br />
    document.getElementById(&quot;div3&quot;).innerHTML = data;<br />
}<br />
[/javascript]</p>
<p>
HTML：
</p>
<p>[html]<br />
&lt;div id=&quot;div1&quot;&gt;getStyle test.&lt;/div&gt;<br />
&lt;div id=&quot;div2&quot;&gt;getStyle test.&lt;/div&gt;<br />
&lt;div id=&quot;div3&quot;&gt;getStyle test.&lt;/div&gt;<br />
&lt;input type=&quot;button&quot; value=&quot;Get data&quot; id=&quot;btn&quot; /&gt;<br />
[/html]</p>
<p>
这三个请求会按顺序执行下来，<a href="http://www.ilovejs.net/lab/queue-ajax/queue-ajax.html">请点击查看具体效果</a>
</p>
<p>下面展示我所实现这个请求队列的Javascript源码：<br />
[javascript]<br />
var ajaxes = []; //用于存储参数对象的队列<br />
//用于声明XMLHttpRequest实例对象<br />
var Xhr = function(){<br />
    var xhr = false;<br />
    try {<br />
        xhr = new XMLHttpRequest();<br />
    }<br />
    catch (e) {<br />
        try {<br />
            xhr = new ActiveXObject(&quot;Msxml2.XMLHTTP&quot;);<br />
        }<br />
        catch (e) {<br />
            xhr = new ActiveXObject(&quot;Microsoft.XMLHTTP&quot;);<br />
        }<br />
    }<br />
    return xhr;<br />
}</p>
<p>var xhr = new Xhr(); //获得XMLHttpRequest实例对象xhr<br />
//executeAjax是主要的执行Ajax的函数<br />
var executeAjax = function(){<br />
    //如果队列为空，则退出执行<br />
    if (!ajaxes.length)<br />
        return;<br />
    var options = ajaxes[0];<br />
    if (xhr) {<br />
        xhr.open(options.method, options.url, true);<br />
        xhr.onreadystatechange = function(){<br />
            if (xhr.readyState === 4 &amp;amp;&amp;amp; (xhr.status === 200 || xhr.status === 304)) {<br />
                options.callback(xhr.responseText, xhr);<br />
                //删除队列中的第一个请求<br />
                ajaxes.shift();<br />
                //如果队列中还有请求，就接着递归执行executeAjax函数，直到队列为空<br />
                if (ajaxes.length &gt; 0) {<br />
                    executeAjax();<br />
                }<br />
            }<br />
        }<br />
        if (xhr.method === &quot;post&quot;) {<br />
            xhr.setRequestHeader(&quot;Content-Type&quot;, &quot;application/x-www-form-urlencoded&quot;);<br />
        }<br />
        xhr.send(options.data || null);<br />
    }<br />
}<br />
//用于添加队列的函数<br />
var addAjax = function(options){<br />
    ajaxes.push(options);<br />
}<br />
[/javascript]</p>
<p>
上面写的或许有些人会认为封装性不好，污染全局变量，如果封装起来也是非常简单的，这只是本人实现了这个效果时候的草稿，放到具体的项目中当然会封装为类或者对象，方便调用，具体的封装后的代码就不提供了。</p>
]]></content:encoded>
			<wfw:commentRss>http://www.ilovejs.net/archives/163/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>说说分支技术的一种替代方案</title>
		<link>http://www.ilovejs.net/archives/146</link>
		<comments>http://www.ilovejs.net/archives/146#comments</comments>
		<pubDate>Sat, 02 Jan 2010 20:14:14 +0000</pubDate>
		<dc:creator>Supersha</dc:creator>
				<category><![CDATA[Ajax]]></category>
		<category><![CDATA[Javascript]]></category>
		<category><![CDATA[lazy definition]]></category>
		<category><![CDATA[分支]]></category>

		<guid isPermaLink="false">http://www.ilovejs.net/?p=146</guid>
		<description><![CDATA[大家应该都知道Javascript中分支技术的实现原理：是一种用来把浏览器间的差异封装到在运行期间进行设置的动态方法中的技术。没错，这个解释是出自《Javascript设计模式》。分支技术最大的特点是只在脚本加载是一次性的确定针对特定的浏览器的代码，这样一来，在初始化完成之后，每种浏览器都只会执行针对特定浏览器的代码，能够在运行时动态确定函数代码的能力。下面是分支技术的简单实现的一个例子： [javascript] //三个对象都声明相同的createXhr方法以示统一 var SimpleXhrFactory=(function(){ var standard={ createXhr:function(){ return new XMLHttpRequest(); } }; var activeXNew={ createXhr:function(){ return new ActiveXObject(&#34;Msxml2.XMLHTTP&#34;); } &#8230; <a href="http://www.ilovejs.net/archives/146" class="more-link">了解更多</a>]]></description>
			<content:encoded><![CDATA[<p>大家应该都知道Javascript中分支技术的实现原理：是一种用来把浏览器间的差异封装到在运行期间进行设置的动态方法中的技术。没错，这个解释是出自《<a  href="http://product.dangdang.com/product.aspx?product_id=20511558">Javascript设计模式</a>》。分支技术最大的特点是只在脚本加载是一次性的确定针对特定的浏览器的代码，这样一来，在初始化完成之后，每种浏览器都只会执行针对特定浏览器的代码，能够在运行时动态确定函数代码的能力。下面是分支技术的简单实现的一个例子：<br />
[javascript]<br />
//三个对象都声明相同的createXhr方法以示统一<br />
var SimpleXhrFactory=(function(){<br />
   var standard={<br />
       createXhr:function(){<br />
            return new XMLHttpRequest();<br />
       }<br />
   };<br />
   var activeXNew={<br />
       createXhr:function(){<br />
            return new ActiveXObject(&quot;Msxml2.XMLHTTP&quot;);<br />
       }<br />
   };<br />
   var activeXOld={<br />
      createXhr:function(){<br />
           return new ActiveXObject(&quot;Microsoft.XMLHTTP&quot;);<br />
      }<br />
   };</p>
<p>   var testObj;<br />
   try{<br />
       testObj=standard.createXhr();<br />
       return standard;<br />
   }catch(e){<br />
        try{<br />
              testObj=activeXNew.createXhr();<br />
              return activeXNew;<br />
        }catch(e){<br />
              testObj=activeXOld.createXhr();<br />
              return activeXNew;<br />
        }<br />
   }<br />
}();<br />
[/javascript]<br />
上面的例子就是分支技术的一个应用。我不知道大家对这段代码是否有什么意见或者异议，反正我是觉得冗长了，无端端的声明了三个对象不说，最后还是需要try&#8230;catch一套。下面是我所想到的一种解决方案来代替这个关于声明XMLHttpRequest实例对象的例子：<br />
[javascript]<br />
//在运行时就确定了特定的浏览器下将要执行哪个分支的函数<br />
var XHR = {}<br />
XHR.create = (function(){<br />
    if (window.XMLHttpRequest) {<br />
        return function(){<br />
            return new XMLHttpRequest();<br />
        }<br />
    }<br />
    else {<br />
        try {<br />
            //为了判断Msxml2和Microsoft版本的ActiveXObject，不得不实例化一个,不过也就是一次性过了而已<br />
            return new ActiveXObject(&quot;Msxml2.XMLHTTP&quot;) &amp;&amp; function(){<br />
                return new ActiveXObject(&quot;Msxml2.XMLHTTP&quot;);<br />
            }<br />
        } catch (e) {<br />
            return function(){<br />
                return new ActiveXObject(&quot;Microsoft.XMLHTTP&quot;);<br />
            }<br />
        }<br />
    }<br />
})();<br />
[/javascript]</p>
<p>
上面的代码简洁明了，有一个不好的缺点就是需要在IE6下需要实例化多一个ActiveXObject(“Msxml2.XMLHTTP”)作为判断条件，IE7以上以及FF等其他浏览器则不会有这个实例化的过程，不过还是那句话，也就是在初始化的时候有这个过程而已，之后浏览器会根据特定的函数来执行。<a  href="http://www.ilovejs.net/lab/xhr.html">测试页面</a>
</p>
<p><span id="more-146"></span><br />
当然了，使用Lazy Definition Pattern的方式来实现也是同样的原理：<br />
[javascript]<br />
var XHR = {}<br />
XHR.create = function(){<br />
    if (window.XMLHttpRequest) {<br />
        XHR.create = function(){<br />
            return new XMLHttpRequest();<br />
        }<br />
    }<br />
    else {<br />
        try {<br />
            //为了判断Msxml2和Microsoft版本的ActiveXObject，不得不实例化一个<br />
            XHR.create = new ActiveXObject(&quot;Msxml2.XMLHTTP&quot;) &amp;&amp;function(){<br />
                return new ActiveXObject(&quot;Msxml2.XMLHTTP&quot;);<br />
            }<br />
        } catch (e) {<br />
            XHR.create = function(){<br />
                return new ActiveXObject(&quot;Microsoft.XMLHTTP&quot;);<br />
            }<br />
        }<br />
    }<br />
    XHR.create();<br />
};<br />
[/javascript]</p>
]]></content:encoded>
			<wfw:commentRss>http://www.ilovejs.net/archives/146/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>JsonP实现跨域</title>
		<link>http://www.ilovejs.net/archives/84</link>
		<comments>http://www.ilovejs.net/archives/84#comments</comments>
		<pubDate>Fri, 25 Dec 2009 03:13:21 +0000</pubDate>
		<dc:creator>Supersha</dc:creator>
				<category><![CDATA[Ajax]]></category>
		<category><![CDATA[Javascript]]></category>
		<category><![CDATA[jsonp]]></category>
		<category><![CDATA[跨域]]></category>

		<guid isPermaLink="false">http://www.ilovejs.net/?p=84</guid>
		<description><![CDATA[JsonP的就是JSON Padding的缩写，它的原理也是非常简单的，利用了script标签的src属性可以链接任何域下的文件的方法，来链接服务器端的一个代理，从而实现了跨域调用数据的方式，下面是它的一个简单的应用： [javascript] //首先编写一个用于执行的方法体，这个方法就是将要在服务器端的代理输出执行的函数 //obj就是获取的跨域数据 &#60;script type=&#34;text/javascript&#34;&#62; var callback = function(obj){ document.getElementById(&#34;div&#34;).innerHTML = obj.message; } &#60;/script&#62; //接下来就需要再声明一个script标签，将它的src属性链接到异域名的代理来获取数据 &#60;script type=&#34;text/javascript&#34; src=&#34;http://www.ilovejs.net/lab/test.php?method=callback&#34;&#62;&#60;/script&#62; &#8230; <a href="http://www.ilovejs.net/archives/84" class="more-link">了解更多</a>]]></description>
			<content:encoded><![CDATA[<p><a href="http://bob.pythonmac.org/archives/2005/12/05/remote-json-jsonp/">JsonP</a>的就是JSON Padding的缩写，它的原理也是非常简单的，利用了script标签的src属性可以链接任何域下的文件的方法，来链接服务器端的一个代理，从而实现了跨域调用数据的方式，下面是它的一个简单的应用：<br />
[javascript]<br />
//首先编写一个用于执行的方法体，这个方法就是将要在服务器端的代理输出执行的函数<br />
//obj就是获取的跨域数据<br />
&lt;script type=&quot;text/javascript&quot;&gt;<br />
var callback = function(obj){<br />
      document.getElementById(&quot;div&quot;).innerHTML = obj.message;<br />
}<br />
&lt;/script&gt;<br />
//接下来就需要再声明一个script标签，将它的src属性链接到异域名的代理来获取数据<br />
&lt;script type=&quot;text/javascript&quot; src=&quot;http://www.ilovejs.net/lab/test.php?method=callback&quot;&gt;&lt;/script&gt;<br />
[/javascript]<br />
上面提及的代理就是test.php文件，它的代码就是生成Javascript格式的执行函数命令：<br />
[php]<br />
&lt;?php<br />
     $method=$_GET[&quot;method&quot;];<br />
     echo($method.&quot;({message:&#8217;message come from server www.ilovejs.net&#8217;});&quot;);<br />
?&gt;<br />
[/php]</p>
<p>
上面就是一个简单的JsonP的应用例子，不过值得说明的是，因为已经把链接到代理的script标签静态的声明了，所以它只能是随页面加载的时候获取异域数据，想要重新获取数据，只能是刷新页面，这个是上面那种方式的其中的一个缺点。要解决这个问题的方法就是动态生成链接到代理的script标签，这样就可以实现局部刷新获取数据，<strong>不过这里也有一个小问题，就是动态生成的这个script标签，需要在下次用户重复局部刷新这个异域获取数据的组件的时候能及时的remove掉，否则head就会囤积了很多丢弃的script标签，造成性能问题</strong>。下面是这种方式的实现方式：
</p>
<p><span id="more-84"></span><br />
[javascript]<br />
//将要执行的函数<br />
var callMethod = function(obj){<br />
    document.getElementById(&quot;div&quot;).innerHTML = obj.message;<br />
}</p>
<p>var getJSONP = function(data, callback){<br />
    var script = document.getElementById(&quot;jsonp&quot;);<br />
    var head = document.getElementsByTagName(&quot;head&quot;)[0];<br />
    //如果已经存在了id为jsonp的script标签，先remove掉，这一步非常重要<br />
    if (script) {<br />
        head.removeChild(script);<br />
    }<br />
    script = document.createElement(&quot;script&quot;);<br />
    script.type = &quot;text/javascript&quot;;<br />
    script.id = &quot;jsonp&quot;;<br />
    //在参数中传递客户端的数据以及将要执行的函数名，再加一个时间戳，可以解除浏览器缓存问题<br />
    script.src = &quot;http://www.ilovejs.net/lab/test.php?method=callMethod&amp;jsonp=&quot; + data + &quot;&amp;date=&quot; + new Date().getTime();<br />
    head.appendChild(script);<br />
    script.onload = function(){<br />
        callback(script);<br />
    }<br />
    script.onreadystatechange = function(){<br />
        if (this.readyState == &#8216;loaded&#8217;) {<br />
            callback(script);<br />
        }<br />
    }<br />
}<br />
[/javascript]<br />
上面展示的代码基本说明了JsonP的基本原理和实现方式，更多利用JsonP实现跨域的复杂的操作都是基于这些基础原理的。</p>
]]></content:encoded>
			<wfw:commentRss>http://www.ilovejs.net/archives/84/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>

