跨域的异步请求三
这部分我们继续增强我们的系统,如强制定的回调函数。参考一些类库,这些回调函数暂定如下几种:
- onStart:请求开始时调用。
- onSuccess:请求成功时调用,通常后台会返回一个JSON给它做参数。
- onTimeout:请求超时时调用,有一个参数,标识其耗时多少毫秒。
- onError:请求失败时调用,有一个异常对象做参数。如果超时,我们会自动抛出一个自定义错误。
- onComplete:请求完成时调用,为了保证它只执行一次,我们会对它进行包装。
我们需要一些工具函数来完成这个系统,如类型判定,深拷贝,迭代器等。这些我以前在博客也讲过了,不详述了!
var dom = {
is:function (obj,type) {
return (type === "Null" && obj === null) ||
(type === "Undefined" && obj === void 0 ) ||
Object.prototype.toString.call(obj).slice(8,-1) === type;
},
noop:function(){},
deepcopy:function(result, source){
for(var key in source) {
var copy = source[key];
if(result === copy) continue;//如window.window === window,会陷入死循环,需要处理一下
if(dom.is(copy,"Object")){
result[key] = arguments.callee(result[key] || {}, copy);
}else if(dom.is(copy,"Array")){
result[key] = arguments.callee(result[key] || [], copy);
}else{
result[key] = copy;
}
}
return result;
},
each : function(obj,fn,bind){
for(var i in obj){
if(obj.hasOwnProperty(i)){
if(fn.call(bind || obj[i],obj[i],i,obj)===false){
break
}
}
}
},
toQueryString : function (obj) {//将对象转换为查询字符串
var enc = encodeURIComponent,
pairs = [],
regexp = /%20/g,
backstop = {};
dom.each(obj,function(value,name){
if(value != backstop[name]){
var assign = enc(name).replace(regexp,'+') + "=";
if(dom.is(value,"Array")){
dom.each(value,function(el){
pairs.push(assign + enc(el).replace(regexp,'+'));
});
}else{
pairs.push(assign + enc(value).replace(regexp,'+'));
}
}
});
return pairs.join("&"); // String
},
head:document.getElementsByTagName("head")[0],
//obj的成员有url,callbackName,callback,data,cache
jsonp:function(){}
}
然后是系统的主体部分了:
dom.jsonp = function(obj){
var self = arguments.callee;
//=============列队处理======================
if (self.callbacks.length) {
setTimeout(function() {self(obj)}, 0);
return;
}
//=============配置对象处理===================
obj = dom.deepcopy(dom.deepcopy({}, dom.jsonp.settings), obj);
var query = dom.toQueryString(obj.data),
url = obj.url+"?"+ query+"&"+obj.callbackName+"=dom.jsonp.callback",
script = document.createElement("script"),
timed_out = false, timeoutID = null,
destroyScript = function(){
script.parentNode && script.parentNode.removeChild( script );
}, old = obj.onComplete;
obj.startTime = new Date-0;
obj.onComplete = function(){//修正onComplete回调%>
if(!arguments.callee.done){
old.call(this,[].slice.call(arguments))
arguments.callee.done = true;
}
}
//========构建新JS文件上的回调函数==================
dom.jsonp.addCallback((function(obj){
return function(){//参数未定,第一个参数肯定为JSON
var args = [].slice.call(arguments)
try{
if (!timed_out){//清除timeout引用
clearTimeout(timeoutID);
}else{
throw new Error(504,"Gateway Timeout")
}
obj.onSuccess.apply(obj,args);
}catch(e){
obj.onError.call(null,e);
}finally{
obj.onComplete.apply(obj,args);
destroyScript();
}
}
})(obj));
//================设置script标签=============
if(!obj.cache)
url+= '×tamp='+(new Date-0)
script.src = url;
dom.head.appendChild(script);
obj.onStart.call(obj,null);
//===========设置请求超时的相关回调============
if (obj.timeout){
timeoutID = setTimeout(function(){
//如果超时则执行如下回调函数
timed_out = true;
var time = new Date - obj.startTime;
obj.onTimeout.call(obj,time);
obj.onComplete.call(obj,null);
destroyScript();
},obj.timeout)
}
};
//静态成员
dom.deepcopy(dom.jsonp, {
settings : {
url:location.href,
callbackName: 'callback',
cache:false,
onStart:dom.noop,
onSuccess:dom.noop,
onComplete:dom.noop,
onTimeout:dom.noop,
onError:dom.noop
},
callbacks : [],
callback :function(){//统一处理回调函数
var objs = this.callbacks,
args = [].slice.call(arguments)
for (var i=0,el;el=objs[i++];) {
el.apply(null,args)
}
this.callbacks = [];
},
addCallback : function(obj){
this.callbacks.push(obj)
}
});
例子中我们做了各种测试,都能过了,不过除IE外,其他浏览器的执行顺序都朋点絮乱了。这缘于标准浏览器并没有对setTimeout的待执行函数弄成一个队列,看来我们要自行搞一个延迟队列才行了……
机器瞎学/数据掩埋/模式混淆/人工智障/深度遗忘/神经掉线/计算机幻觉/专注单身二十五年
浙公网安备 33010602011771号