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

 //读取文件的原始内容
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>

一步一步实现基于Task的Promise库(一)Promise的基本实现的更多相关文章

  1. 通过Dapr实现一个简单的基于.net的微服务电商系统(五)——一步一步教你如何撸Dapr之状态管理

    状态管理和上一章的订阅发布都算是Dapr相较于其他服务网格框架来讲提供的比较特异性的内容,今天我们来讲讲状态管理. 目录:一.通过Dapr实现一个简单的基于.net的微服务电商系统 二.通过Dapr实 ...

  2. 通过Dapr实现一个简单的基于.net的微服务电商系统(六)——一步一步教你如何撸Dapr之Actor服务

    我个人认为Actor应该是Dapr里比较重头的部分也是Dapr一直在讲的所谓"stateful applications"真正具体的一个实现(个人认为),上一章讲到有状态服务可能很 ...

  3. 通过Dapr实现一个简单的基于.net的微服务电商系统(七)——一步一步教你如何撸Dapr之服务限流

    在一般的互联网应用中限流是一个比较常见的场景,也有很多常见的方式可以实现对应用的限流比如通过令牌桶通过滑动窗口等等方式都可以实现,也可以在整个请求流程中进行限流比如客户端限流就是在客户端通过随机数直接 ...

  4. 通过Dapr实现一个简单的基于.net的微服务电商系统(九)——一步一步教你如何撸Dapr之OAuth2授权

    Oauth2授权,熟悉微信开发的同学对这个东西应该不陌生吧.当我们的应用系统需要集成第三方授权时一般都会做oauth集成,今天就来看看在Dapr的语境下我们如何仅通过配置无需修改应用程序的方式让第三方 ...

  5. 通过Dapr实现一个简单的基于.net的微服务电商系统(十)——一步一步教你如何撸Dapr之绑定

    如果说Actor是dapr有状态服务的内部体现的话,那绑定应该是dapr对serverless这部分的体现了.我们可以通过绑定极大的扩展应用的能力,甚至未来会成为serverless的基础.最开始接触 ...

  6. 一步一步学ZedBoard & Zynq(四):基于AXI Lite 总线的从设备IP设计

    本帖最后由 xinxincaijq 于 2013-1-9 10:27 编辑 一步一步学ZedBoard & Zynq(四):基于AXI Lite 总线的从设备IP设计 转自博客:http:// ...

  7. 通过Dapr实现一个简单的基于.net的微服务电商系统(四)——一步一步教你如何撸Dapr之订阅发布

    之前的章节我们介绍了如何通过dapr发起一个服务调用,相信看过前几章的小伙伴已经对dapr有一个基本的了解了,今天我们来聊一聊dapr的另外一个功能--订阅发布 目录:一.通过Dapr实现一个简单的基 ...

  8. 通过Dapr实现一个简单的基于.net的微服务电商系统(三)——一步一步教你如何撸Dapr

    目录:一.通过Dapr实现一个简单的基于.net的微服务电商系统 二.通过Dapr实现一个简单的基于.net的微服务电商系统(二)--通讯框架讲解 三.通过Dapr实现一个简单的基于.net的微服务电 ...

  9. 通过Dapr实现一个简单的基于.net的微服务电商系统(八)——一步一步教你如何撸Dapr之链路追踪

    Dapr提供了一些开箱即用的分布式链路追踪解决方案,今天我们来讲一讲如何通过dapr的configuration来实现非侵入式链路追踪的 目录:一.通过Dapr实现一个简单的基于.net的微服务电商系 ...

  10. 通过Dapr实现一个简单的基于.net的微服务电商系统(十一)——一步一步教你如何撸Dapr之自动扩/缩容

    上一篇我们讲到了dapr提供的bindings,通过绑定可以让我们的程序轻装上阵,在极端情况下几乎不需要集成任何sdk,仅需要通过httpclient+text.json即可完成对外部组件的调用,这样 ...

随机推荐

  1. ZOJ1610_Count the Colors(段树/为段更新)

    解决报告 意甲冠军: 一定长度8000段染.寻求染色完成后,.. 思路: 区间问题用线段树.成段的更新区间.最后把全部的区间下压到叶子结点,统计叶子结点的颜色. #include <iostre ...

  2. ruby简单的基本 6

    模 像类似的模块,那里 class method 和 instance method.module 没有new不能生成对象的例子其中 class method 所谓的模块在模块化的方法,它能够直接调用 ...

  3. mysql只导出表结构或数据

    唯一的非导电结构指南数据 mysqldump -t 数据库名称 -uroot -p > xxx.sql 指南结构不仅指导数据 mysqldump    --opt -d  数据库名 -u -p ...

  4. 一位OWin服务器新成员TinyFox

    一位OWin服务器新成员TinyFox TinyFox 是一款支持OWIN标准的WEB应用的高性能的HTTP服务器,是Jexus Web Server的"姊妹篇".TinyFox本 ...

  5. Mysql编写定时任务事件

    原文:Mysql编写定时任务事件 场景: 例如:某系统,用户每天只能拥有一次的抽奖机会,抽过后当天就不可再抽,但是过了24:00点后用户就重新拥有一次抽奖机会.像这种需要数据库定时对某个字段进行更新操 ...

  6. HDU 4932 Miaomiao&#39;s Geometry(推理)

    HDU 4932 Miaomiao's Geometry pid=4932" target="_blank" style="">题目链接 题意: ...

  7. C#实现对mongoDB的简单增删查改

    首先添加所需要驱动包(可通过nuget获得) using MongoDB.Bson;using MongoDB.Driver;using MongoDB.Driver.Builders; 一.设置配置 ...

  8. 操作jQuery集合搜索父元素

    搜索父元素 1.1parents()方法 parents()方法用于获取u当前匹配元素集合中的每个元素的祖先元素,根据需要还可以使用一个选择器进行筛选parents([selector]) 其中sel ...

  9. oracle删除重复记录,只保留rowid最小的记录

    初探oracle删除重复记录,只保留rowid最小的记录   如题,初探oracle删除重复记录,只保留rowid最小的记录(rowid可以反映数据插入到数据库中的顺序) 一.删除重复记录可以使用多种 ...

  10. HDU 4228 Flooring Tiles 反素数

    推出了结论,万万没想到最后用搜索.. 还想dp来着.. #include <cstdio> #include <cstring> #include <iostream&g ...