如果我们现在有一个需求,大概是先读取一个文件的内容,再把得到的内容传给后台去解析,最后把解析后的结果再保存到那个文件,按照最原始的做法代码就是下面这个样子的:

 //读取文件的原始内容
var readFile = function(fileName, callback){
window.setTimeout(function(){
console.log("read '" + fileName + "' complete.");
var rawContent = "..... content ......";
if(callback){
callback(rawContent);
}
}, 2000);
};
//请求服务器来解析原始内容,得到真正的内容
var resolveFile = function(serverUrl, rawContent, callback){
window.setTimeout(function(){
console.log("resolve complete.");
var realContent = "..... 内容 .....";
if(callback){
callback(realContent);
}
}, 1000);
};
//把真正的内容写入一开始的文件
var writeBack = function(fileName, realContent, callback){
window.setTimeout(function(){
console.log("writeBack complete.");
if(callback){
callback();
}
}, 2000);
};
readFile("aa.txt", function(rawContent){
resolveFile("/service/fileResolve.ashx", rawContent, function(realContent){
writeBack("aa.txt", realContent, function(){
//给个完工的通知
alert("everything is ok.");
});
});
});

这里我全部采用window.setTimeout来模拟一个异步操作,然而这种嵌套回调方法的做法看起来非常丑陋,如果能改掉嵌套的形式,采用链式调用会美观很多。因此我们期望的调用形式是下面这个样子:

 //期望的调用形式(一) Promise的基本实现
var taskExp1_1 = new Task(readFile, ["aa.txt"])
.then(resolveFile, ["/service/fileResolve.ashx"])
.then(writeBack, ["aa.txt"])
.then(function(){
alert("everything is ok.");
this.end();
})
//do方法才是正真的执行这一组异步调用,不调用do方法相当于只是配置一组异步调用
.do();

这种异步方法的链式调用实际上就是一个Promise的实现,只不过这里是通过一个Task类去完成的,我们的目标就是实现这个Task类,它包含了一组有先后逻辑依赖的异步操作,then方法里面传递的function并不会因为then的执行而执行,实际上then方法可以看做是对一组有先后逻辑依赖的异步操作的一个配置,真正导致执行的是do方法,调用do方法会从这个队列的头部开始调用,而标致一个异步操作的结束是在异步操作方法里面,看下面的代码:

 //读取文件的原始内容
var readFile = function(fileName){
var _this = this;
window.setTimeout(function(){
var rawContent = "xxxxxxxx (" + fileName + ")";
console.log("read '" + fileName + "' complete. rawContent is " + rawContent);
//告知异步调用已经完成
_this.end(rawContent);
}, 2000);
};

我们稍微对readFile方法做了修改,当文件读取完成的时候调用this.end方法通知异步操作的完成,这样Task就知道该进行下一个异步操作了,就会执行resolveFile方法,那么这里有一个问题就是readFile方法需要传递一个参数rawContent给resolveFile方法,可以看到this.end(rawContent);这句代码已经有传递,resolveFile方法如何接收呢?

 //请求服务器来解析原始内容,得到真正的内容
var resolveFile = function(serverUrl){
var _this = this;
//可以从params属性中获取上一个异步调用传递过来的参数
var rawContent = _this.params;
window.setTimeout(function(){
var realContent = "Greeting (" + serverUrl + ")";
console.log("resolve file complete. realContent is " + realContent);
_this.end(realContent);
}, 1000);
};

可以看到resolveFile方法通过this.params接收readFile的输出参数。

到目前为止,我们看到的都是如何使用Task类,那么我们最希望有一个什么样的库来完成这种逻辑配置关系呢? 除了上面说的传参问题,还有一个就是我希望每一个异步操作都可以接收一些形参,这样我们使用Task类的时候就不用自己拐弯抹角的塞参数了,否则我们可能要这样写:

 var taskExp1_1 = new Task(function (){
readFile.call(this, "aa.txt");
}).then(function (){
resolveFile.call(this, "/service/fileResolve.ashx");
}).then(function (){
writeBack.call(this, "aa.txt");
}).then(function () {
alert("everything is ok.");
this.end();
})
.do();

下面是整个demo和Task类的实现细节:

 <script type="text/javascript">
//读取文件的原始内容
var readFile = function(fileName){
var _this = this;
window.setTimeout(function(){
var rawContent = "xxxxxxxx (" + fileName + ")";
console.log("read '" + fileName + "' complete. rawContent is " + rawContent);
_this.end(rawContent);
}, 2000);
};
//请求服务器来解析原始内容,得到真正的内容
var resolveFile = function(serverUrl){
var _this = this;
var rawContent = _this.params;
window.setTimeout(function(){
var realContent = "Greeting (" + serverUrl + ")";
console.log("resolve file complete. realContent is " + realContent);
_this.end(realContent);
}, 1000);
};
//把真正的内容写入一开始的文件
var writeBack = function(fileName){
var _this = this;
var realContent = _this.params;
window.setTimeout(function(){
console.log("writeBack complete.");
_this.end();
}, 2000);
};
var WorkItem = function(func, args){
return {
//表示执行此异步操作的先决条件
condition: "",
//表示当前异步操作是否执行完了
isDone: false,
//正真的执行
'do': function(context){
func.call(context, args);
}
};
};
var Task = function(func, args){
//Task内部会维护一个异步方法的队列,此队列严格按照先后顺序执行
var wItemQueue = [];
//当前异步方法
var currentItem;
//执行异步方法前要判断的先决条件集合(目前只有then)
var condition = {
//直接执行
then: function(workItem){
return true;
}
};
//初始化一个异步操作,这个方法主要处理接受参数的多样性
var _initWorkItem = function(func, args, condition){
if(func instanceof Task){
return null;
}
else{
return _enqueueItem(new WorkItem(func, args), condition);
}
};
//记录异步操作的先决条件,并添加到队列中去
var _enqueueItem = function(item, condition){
if(condition){
item.condition = condition;
}
wItemQueue.push(item);
return item;
};
//试着执行下一个异步操作,如果这个操作满足他的先决条件,那就执行
var _tryDoNextItem = function(context){
var next = _getCurNextItem();
if(next){
if(condition[next.condition](next)){
currentItem = next;
currentItem.do(context);
}
}
};
//获取下一个异步操作,如果已经是最后一个了返回undefined
var _getCurNextItem = function(){
var i=0;
for(; i<wItemQueue.length; i++){
if(currentItem == wItemQueue[i]){
break;
}
}
return wItemQueue[i + 1];
};
//定义异步操作的上下文环境
var Context = function(){};
Context.prototype = {
//上一个异步调用传递过来的参数
'params': null,
//执行此方法就表示当前异步操作已经完成,那么会尝试执行下一个异步操作
end: function(output){
currentItem.isDone = true;
this.params = output;
_tryDoNextItem(this);
return this;
}
};
currentItem = _initWorkItem(func, args); //Task的公共方法,这些方法都应该支持链式调用(都返回this)
return {
//开始执行
'do': function(){
if(currentItem && currentItem.condition == ""){
currentItem.do(new Context());
}
return this;
},
//配置下一个异步操作
then: function(func, args){
_initWorkItem(func, args, 'then');
return this;
}
};
}; var taskExp_1 = new Task(readFile, ["aa.txt"])
.then(resolveFile, ["/service/fileResolve.ashx"])
.then(writeBack, ["aa.txt"])
.then(function(){
alert("everything is ok.");
this.end();
})
.do();
</script>

最新文章

  1. python 静态方法、类方法(二)
  2. Python 学习拾遗
  3. redis 学习笔记(1)-编译、启动、停止
  4. [Python]命令行进度条
  5. mysqldump备份详解
  6. Oracle10g_Dataguard__161031
  7. mysql 中关于周和月份的表示
  8. Canvas修行之黑客帝国代码雨
  9. [POJ2348]Euclid&#39;s Game
  10. OpenCV ——背景建模之CodeBook(1)
  11. h1b期间回国须知
  12. rsync随机启动脚本
  13. yii2 源码分析 model类分析 (五)
  14. OpenGL 背面剔除
  15. list标准函数的模拟
  16. Linux kernel的中断子系统之(六):ARM中断处理过程
  17. python控制台输出带颜色的文字方法
  18. oracle中的insert all into,在mysql中的写法
  19. discuz config_global.php文件设置说明
  20. MySQL 常用使用语句

热门文章

  1. php异或加密解密算法的实现
  2. 光流和KLT
  3. poj1083 思考题
  4. HDOJ 3037 Saving Beans
  5. POJ3690 Constellations 【KMP】
  6. RPC框架实现
  7. MVC6项目
  8. Android基础之——MacOSX下elipse开发环境的配置
  9. 从头开始学JavaScript(一)——基础中的基础
  10. 程序员的Scala