开始前的准备
var events = require("events");
var emitter = new events.EventEmitter();
var async = false;
emitter.on('eventType', function(){
console.assert(async);
});
emitter.emit('eventType'); //报错false == true
async = true;
然后是setTimeout实现的异步
var async = false;
setTimeout(function(){
console.assert(async); //没有报错
}, 0);
async = true;
var serializer = require('./io/form-serializer');
var IO = require('./io/base');
var util = require('util');
require('./io/xhr-transport');
require('./io/script-transport');
require('./io/jsonp');
require('./io/form');
require('./io/iframe-transport');
require('./io/methods');
jsonp: function (url, data, callback) {
if (typeof data === 'function') {
callback = data;
data = undefined;
}
return get(url, data, callback, 'jsonp')
}
getScript: require.load
function get(url, data, callback, dataType, type) {
// data 参数可省略
if (typeof data === 'function') {
dataType = callback;
callback = data;
data = undefined;
}
return IO({
type: type || 'get',
url: url,
data: data,
complete: callback,
dataType: dataType
});
}
意料之中的调用IO,dataType设置为jsonp,get请求。其它参数都和一般ajax参数一致,dataType显然线索啦。特地在getScript:require.load留了心,因为getScript负责script发送工作,而jsonp它本身也是以一种getScript。
var IO = require('./base'); 那就去base.js中看看
总结:IO.js作用就是将各个js文件载入,接着给IO添加几个便捷入口,最后统一调用IO函数。
base.js
首先我们肯定会被defaultConfig吸引目光,因为这里设置了Ajax所需的各种默认配置,HTTP请求mothods、MIME类型、编码等,一般的转换器设置也都在这个对象了。
接着是设置配置的函数setUpConfig(c),在我们不知道会传入什么参数前还是不看了(其实看看也可以,看看代码也能猜个八九不离十)。
那么直接找到IO函数,参数是c。self就是IO函数的一个实例。然后给IO套上promise模式,可以用起链式操作来啦~(我也是看yiminghe菊苣的注释乐的! ‘2012-2-07 yiminghe@gmail.com - 返回 Promise 类型对象,可以链式操作啦!’卧槽....好萌!)。self.userConfig = c;c = setUpConfig(c); 我们的参数现在传给了setConfig并生成新的参数对象。为了不中断对IO函数的分析。我们先往后分析。util.mix是将后面的对象clone到self,然后就添加了一些诸如config,transport,timeoutTimer一堆属性。再往后,声明传送器构造函数、传送器变量,然后触发start事件!这标志着IO的开始!后面接着是根据dataType选择对应的传送器并创建实例。然后是Ajax相关的Header、readyState、status等设置,这和jsonp没有关系。然后它用setTimeout模仿了Timeout功能。再往后也没什么了。base.js的最后我们又往IO函数添加了像setupConfig,setupTransport,getTransport这些方法,来实现config设置等功能。而我们在意的是setupTransport,传送器设置。
现在我们回过头来看setupConfig。它的返回值是一个新的config对象。
在一开始用把参数c深度clone到defaultConfig,结合之后产生所需的config赋值给c,像这样
var context = c.context;
delete c.context;
c = util.mix(util.clone(defaultConfig), c, {
deep: true
});
c.context = context || c;
context代表的是请求后回调函数的“环境”(context傻瓜翻译)——success,error,complete。后面声明变量什么的略过,uri = c.uri,是一个包含url所有相关参数的url对象。uri.query由url中querystring的名/值对构成JSON格式对象/数组。我们排除与跨域无关的处理过程,jsonp大多都是没有的啊~
dataType = c.dataType = util.trim(dataType || '*').split(rspace);
if (!('cache' in c) && util.inArray(dataType[0], ['script', 'jsonp'])) {
c.cache = false;
}
util.trim去掉dataType前后空格,rspace = /s+/,split按空格分割dataType成子字符串数组。接着确认dataType为jsonp。c.cache标识是否是调用script或jsonp发送器,false即代表是.它的作用:url会添加uri.query._ksTS = util.now() + '_' + util.guid(); 也就是 ksTS=时间戳_全局唯一标示符,guid函数的作用就是生成一个全局唯一标示符,英文一般叫UUID或是GUID。
到这里,url处理就完成了。现在还是一个对象。
接着就start! start事件有很多事件处理器,它们在执行前都会有判断,我们手持dataType['jsonp']开始jsonp.js部分。
jsonp.js
首先需要注意的是
IO.setupConfig({
jsonp: 'callback',
jsonpCallback: function () {
// 不使用 now() ,极端情况下可能重复
return util.guid('jsonp');
}
});
我们回到base.js,下面就是准备发送了。
TransportConstructor = transports[c.dataType[0]] || transports['*'];
transport = new TransportConstructor(self);
dataType = c.dataType = util.trim(dataType || '*').split(rspace);
if (!('cache' in c) && util.inArray(dataType[0], ['script', 'jsonp'])) {
c.cache = false;
}
var prevType = dataType[0];
for (var i = 1; i < dataType.length; i++) {
type = dataType[i];
var converter = converts[prevType] && converts[prevType][type];
if (!converter) {
throw new Error('no covert for ' + prevType + ' => ' + type);
}
responseData = converter(responseData);
prevType = type;
}
io.responseData = responseData;
<script>
(function() {
var APIurl = 'http://api.flickr.com/services/feeds/photos_public.gne';
var onSuccess = function (data) {
console.log('get data:', data);
}
require(['io'], function(IO){
IO({
url: APIurl,
type: 'GET',
dataType: 'jsonp',
jsonp: 'jsoncallback',
jsonpCallback: 'ctest',
data: {
tags: "water",
tagmode: "any",
format: "json"
},
complete: onSuccess
});
});
})()
</script>
最后的碎碎念