一步一步实现基于Task的Promise库(二)all和any方法的设计和实现
在上一篇中我们已经初步完成了Task类,如果仅仅是这些,那么没有多大意义,因为网上这类js库有很多,现在我们来些更复杂的使用场景。
如果我们现在有这样一个需求:我们要先读取aa.txt的内容,然后去后台解析,同时bb.txt也要读取解析,然后当两个文件都解析完了,我们还要合并两部分内容存到cc.txt中,最后发个通知说ok了。。需求很变态,但是我还是想问有没有好的办法呢?按照最原始的嵌套回调的写法好像不是那么容易了,因为你没法知道aa.txt和bb.txt两个文件的读取解析谁先完成,所以你除了要关注逻辑本身还得花费一些功夫在这个谁先谁后上面,如果需求变了。。。然后就没有然后了,想想我就要吐血。
那么回到上一章的Task类,Task有没有办法解决这个问题呢?回答是有,先看下面的代码:
var taskExp_1 = new Task(readFile, ["aa.txt"]).then(resolveFile, ["/service/fileResolve.ashx?file=aa.txt"]);
var taskExp_2 = new Task(readFile, ["bb.txt"]).then(resolveFile, ["/service/fileResolve.ashx?file=bb.txt"]);
var taskExp_3 = new Task(taskExp_1, taskExp_2).all(writeFile, ["cc.txt"]).then(sendMail).start();
如同需求描述的一样,taskExp_1,taskExp_2分别描述了aa.txt和bb.txt的读取和解析,taskExp_3的构造方法里面包括了taskExp_1,taskExp_2,后面的all方法表示需要taskExp_1和taskExp_2都完成才能执行writeFile,最后才是sendMail。这样一来彻底解决了这类需求带来的复杂性,如果把all改成any表示taskExp_1和taskExp_2完成其中一个就能执行writeFile。
在实现all和any方法之前,先看一看我们将要面临哪些问题:
- 现在new Task(),then(),all(),any()方法的形参可以是多个异步操作了,并且每个异步操作可以是一个方法和它的参数,也可以是一个Task。
- 既然这些方法的形参可以是Task,那么我们理所当然要支持Task的完成通知。
- 一组异步操作的返回值如何传递到下一组异步操作 ,对于all方法如何接收前一组异步操作的所有输出参数呢?在上一章里我们通过this.param接收参数,这里对于then,any方法同样如此,因为最终它们只会有一个异步操作的输出参数传递到下一个异步操作,all方法比较特殊,我斟酌好久决定使用this.param[0],this.param[1]... 来接收同组的不同异步操作的输出参数
- 当不同状态的Task作为参数传递到另一个Task中时(未开始执行的,正在执行的,执行完成的),应该怎么处理?我们还得支持Task的状态管理。
继续上一章所写的Task类来扩展这两个方法,现在我们以一个标准的js库形式来书写实现细节,这个库命名为Task.js:
(function(){
var isFunction = function(target){
return target instanceof Function;
};
var isArray = function(target){
return target instanceof Array;
}; //自定义事件管理(代码摘抄自http://www.cnblogs.com/dolphinX/p/3254017.html)
var EventManager = function(){
this.handlers = {};
};
EventManager.prototype = {
constructor: EventManager,
addHandler: function(type, handler){
if(typeof this.handlers[type] == 'undefined'){
this.handlers[type] = new Array();
}
this.handlers[type].push(handler);
},
removeHandler: function(type, handler){
if(this.handlers[type] instanceof Array){
var handlers = this.handlers[type];
for(var i=0; i<handlers.length; i++){
if(handler[i] == handler){
handlers.splice(i, 1);
break;
}
}
}
},
trigger: function(type, event){
/*
if(!event.target){
event.target = this;
}
*/
if(this.handlers[type] instanceof Array){
var handlers = this.handlers[type];
for(var i=0; i<handlers.length; i++){
handlers[i](event);
}
}
}
}; //WorkItem的参数和Task一样,有下面几种形式,不过最终存到subItems里面的元素只有两种类型.一种是方法和它的参数,一种是Task对象
//1.(func, [funcArg1, funcArg2])
//2.(func, funcArg1, funcArg2, ....)
//3.(task1, task2, ....)
//4.([func, funcArg1, funcArg2, ....]
// ,[func, [funcArg1, funcArg2]]
// ,task1
// , ....
// )
var WorkItem = function(arrayArgs){
//WorkItem其实是一组异步操作的集合,_subItems就是这个集合
var _subItems = [];
var _checkFunc = function(args){
if(isFunction(args[0])){
if(args.length == 2 && isArray(args[1])){
_subItems.push({'isFunc': true, 'func': args[0], 'args': args[1]});
}
else{
_subItems.push({'isFunc': true, 'func': args[0], 'args': args.slice(1)});
}
return true;
}
return false;
};
var _checkTask = function(task){
if(task instanceof Task){
_subItems.push({'isFunc': false, 'task': task});
}
};
//这里是对形参的检测,看看是否满足上面的4种形式
if(!_checkFunc(arrayArgs)){
for(var i=0; i<arrayArgs.length; i++){
if(!_checkFunc(arrayArgs[i])){
_checkTask(arrayArgs[i]);
}
}
} //开始执行SubItem
var _startSubItem = function(subItemIndex, context){
var subItem = _subItems[subItemIndex];
//如果subItem是方法和它的参数
if(subItem.isFunc){
//先获取方法的上下文环境(就是方法里面的this)
var workItemCxt = context.getWorkItemContext(subItemIndex);
//再执行方法
subItem.func.apply(workItemCxt, subItem.args);
}
//如果subItem是Task对象
else{
//如果task已经完成
if(subItem.task.getStatus() == TaskStatus.finished){
context.end(subItem.task.getOutput(), subItemIndex)
}
else{
//先注册Task对象的完成事件
subItem.task.finished(function(output){
context.end(output, subItemIndex);
});
//再启动这个task
subItem.task.start(context.inputParams);
}
}
};
this.condition = "";
//开始执行WorkItem
this.start = function(context){
context.setItemsCount(_subItems.length);
for(var i=0; i<_subItems.length; i++){
_startSubItem(i, context);
}
}
}; //异步操作上下文
var Context = function(endCallback, inputParams){
var _this = this;
//Workitem里面的每一个Item会按各自的索引把返回值存到_rawOutputParams里,这样做的目的是为了方便后续操作的先决条件判断
var _rawOutputParams = [];
//子Item个数
var _itemCount;
//先决条件的判断方法集合,判断的同时也会更新outputParams
var _condition = {
then: function(){
_this.outputParams = _rawOutputParams[0].value;
return true;
},
all: function(){
_this.outputParams = [];
for(var i=0; i<_itemCount; i++){
if(_rawOutputParams[i]){
_this.outputParams[i] = _rawOutputParams[i].value;
}
else{
return false;
}
}
return true;
},
any: function(){
for(var i=0; i<_itemCount; i++){
if(_rawOutputParams[i]){
_this.outputParams = _rawOutputParams[i].value;
return true;
}
}
return false;
}
}; //异步操作的输入操作
this.inputParams = inputParams;
//最终异步操作上下文结束时的输出参数(一般是一个操作的输出参数,all条件的输出参数比较特殊,是一个数组,每一个元素对应每一个子Workitem的输出参数)
this.outputParams = null;
this.setItemsCount = function(itemCount){
_itemCount = itemCount;
};
//测试_rawOutputParams对于key条件是否通过
this.testCondition = function(key){
return _condition[key]();
};
//索引为index的子Workitem完成时会调用这个方法
this.end = function(output, index){
_rawOutputParams[index] = {
value: output
};
if(endCallback){
endCallback(output);
}
};
//生成一个子Workitem执行时的上下文环境
this.getWorkItemContext = function(index){
//这个是子Item的上下文对象(就是this)
return {
//传递给上下文的参数
param: _this.inputParams,
//调用end方法告知异步操作的完成
end: function(output){
_this.end(output, index);
}
};
};
}; //Task的状态
var TaskStatus = {
//未开始
pending: 0,
//正在进行
doing: 1,
//已完成
finished: 2
}; //不定义具体形参,直接使用arguments
window.Task = function(){
var _status = TaskStatus.pending;
var _wItemQueue = [], _currentItem, _currentContext;
var _eventManager = new EventManager();
var _output;
//初始化一个WorkItem,并添加到执行队列中
var _initWorkItem = function(args){
var arrayArgs = [];
for(var i=0; i<args.length; i++){
arrayArgs[i] = args[i];
}
var item = new WorkItem(arrayArgs);
_wItemQueue.push(item);
return item;
};
//设置item的先决条件
var _setItemCondition = function(item, condition){
if(condition){
item.condition = condition;
}
};
//试着执行下一个异步操作,如果这个操作满足他的先决条件,那就执行;如果已经式最后一个异步操作了,就触发完成事件
var _tryDoNextItem = function(output){
var next = _getCurNextItem();
if(next){
if(_currentContext.testCondition(next.condition)){
_currentItem = next;
_doCurrentItem();
}
}
else{
_status = TaskStatus.finished;
_output = output;
_eventManager.trigger("finish", output);
}
};
//执行当前的Workitem
var _doCurrentItem = function(contextParam){
if(contextParam) {
_currentContext = new Context(_tryDoNextItem, contextParam);
}
else{
if(_currentContext){
_currentContext = new Context(_tryDoNextItem, _currentContext.outputParams);
}
else{
_currentContext = new Context(_tryDoNextItem);
}
}
_currentItem.start(_currentContext);
};
//获取下一个异步操作,如果已经是最后一个了返回undefined
var _getCurNextItem = function(){
var i=0;
for(; i<_wItemQueue.length; i++){
if(_currentItem == _wItemQueue[i]){
break;
}
}
return _wItemQueue[i + 1];
};
_currentItem = _initWorkItem(arguments); //获取Task当前状态
this.getStatus = function(){
return _status;
};
//获取Task完成时的输出参数
this.getOutput = function(){
return _output;
};
//注册完成事件
this.finished = function(callback){
if(callback){
_eventManager.addHandler("finish", callback);
}
};
//任务开始(把do改成start是因为在ie下有问题,ie会把do当做保留字)
//contextParam是一个上下文参数,可以在异步方法中通过this.Param引用
this.start = function(contextParam){
if(_status == TaskStatus.pending){
_status = TaskStatus.doing;
_doCurrentItem(contextParam);
}
return this;
};
this.then = function(){
var workItem = _initWorkItem(arguments);
_setItemCondition(workItem, 'then');
return this;
};
this.all = function(){
var workItem = _initWorkItem(arguments);
_setItemCondition(workItem, 'all');
return this;
};
this.any = function(){
var workItem = _initWorkItem(arguments);
_setItemCondition(workItem, 'any');
return this;
};
};
})();
除了之前的Task类之外,还添加了WorkItem类用来表示一组异步操作。代码有点多,细节请看注释。
最后给一个demo:
<!DOCTYPE html>
<html>
<head>
<title></title>
</head>
<body>
<script src="jquery-latest.js" type="text/javascript"></script>
<script type="text/javascript">
//promise
//读取文件的原始内容
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);
}, 1000);
};
//请求服务器来解析原始内容,得到真正的内容
var resolveFile = function(serverUrl){
var _this = this;
var rawContent = _this.param;
window.setTimeout(function(){
var realContent = "Greeting (" + serverUrl + ")";
console.log("resolve file complete. realContent is " + realContent);
_this.end(realContent);
}, 1500);
};
//把真正的内容写入一开始的文件
var writeFile = function (fileName) {
var _this = this;
window.setTimeout(function(){
console.log("writeBack1 param[0] is " + _this.param[0] + " ;param[1] is " + _this.param[1]);
_this.end();
}, 2000);
};
var sendMail = function(){
var _this = this;
window.setTimeout(function(){
console.log("sendMail finished");
_this.end();
}, 1000);
}; //问题:
//1.WorkItem的参数多样化 (一些没有意义的使用形式?)
//2.new Task(),then,all,any方法参数可以接收Task对象 (Task完成的事件通知?)
//3.一组异步操作的返回值如何传递到下一组异步操作 (对于all方法如何接收前一组异步操作的所有输出参数)
//4.当不同状态的Task作为参数传递到另一个Task中时(未开始执行的,正在执行的,执行完成的),应该怎么处理 (Task状态管理)
(function(){
var isFunction = function(target){
return target instanceof Function;
};
var isArray = function(target){
return target instanceof Array;
}; //自定义事件管理(代码摘抄自http://www.cnblogs.com/dolphinX/p/3254017.html)
var EventManager = function(){
this.handlers = {};
};
EventManager.prototype = {
constructor: EventManager,
addHandler: function(type, handler){
if(typeof this.handlers[type] == 'undefined'){
this.handlers[type] = new Array();
}
this.handlers[type].push(handler);
},
removeHandler: function(type, handler){
if(this.handlers[type] instanceof Array){
var handlers = this.handlers[type];
for(var i=0; i<handlers.length; i++){
if(handler[i] == handler){
handlers.splice(i, 1);
break;
}
}
}
},
trigger: function(type, event){
/*
if(!event.target){
event.target = this;
}
*/
if(this.handlers[type] instanceof Array){
var handlers = this.handlers[type];
for(var i=0; i<handlers.length; i++){
handlers[i](event);
}
}
}
}; //WorkItem的参数和Task一样,有下面几种形式,不过最终存到subItems里面的元素只有两种类型.一种是方法和它的参数,一种是Task对象
//1.(func, [funcArg1, funcArg2])
//2.(func, funcArg1, funcArg2, ....)
//3.(task1, task2, ....)
//4.([func, funcArg1, funcArg2, ....]
// ,[func, [funcArg1, funcArg2]]
// ,task1
// , ....
// )
var WorkItem = function(arrayArgs){
//WorkItem其实是一组异步操作的集合,_subItems就是这个集合
var _subItems = [];
var _checkFunc = function(args){
if(isFunction(args[0])){
if(args.length == 2 && isArray(args[1])){
_subItems.push({'isFunc': true, 'func': args[0], 'args': args[1]});
}
else{
_subItems.push({'isFunc': true, 'func': args[0], 'args': args.slice(1)});
}
return true;
}
return false;
};
var _checkTask = function(task){
if(task instanceof Task){
_subItems.push({'isFunc': false, 'task': task});
}
};
//这里是对形参的检测,看看是否满足上面的4种形式
if(!_checkFunc(arrayArgs)){
for(var i=0; i<arrayArgs.length; i++){
if(!_checkFunc(arrayArgs[i])){
_checkTask(arrayArgs[i]);
}
}
} //开始执行SubItem
var _startSubItem = function(subItemIndex, context){
var subItem = _subItems[subItemIndex];
//如果subItem是方法和它的参数
if(subItem.isFunc){
//先获取方法的上下文环境(就是方法里面的this)
var workItemCxt = context.getWorkItemContext(subItemIndex);
//再执行方法
subItem.func.apply(workItemCxt, subItem.args);
}
//如果subItem是Task对象
else{
//如果task已经完成
if(subItem.task.getStatus() == TaskStatus.finished){
context.end(subItem.task.getOutput(), subItemIndex)
}
else{
//先注册Task对象的完成事件
subItem.task.finished(function(output){
context.end(output, subItemIndex);
});
//再启动这个task
subItem.task.start(context.inputParams);
}
}
};
this.condition = "";
//开始执行WorkItem
this.start = function(context){
context.setItemsCount(_subItems.length);
for(var i=0; i<_subItems.length; i++){
_startSubItem(i, context);
}
}
}; //异步操作上下文
var Context = function(endCallback, inputParams){
var _this = this;
//Workitem里面的每一个Item会按各自的索引把返回值存到_rawOutputParams里,这样做的目的是为了方便后续操作的先决条件判断
var _rawOutputParams = [];
//子Item个数
var _itemCount;
//先决条件的判断方法集合,判断的同时也会更新outputParams
var _condition = {
then: function(){
_this.outputParams = _rawOutputParams[0].value;
return true;
},
all: function(){
_this.outputParams = [];
for(var i=0; i<_itemCount; i++){
if(_rawOutputParams[i]){
_this.outputParams[i] = _rawOutputParams[i].value;
}
else{
return false;
}
}
return true;
},
any: function(){
for(var i=0; i<_itemCount; i++){
if(_rawOutputParams[i]){
_this.outputParams = _rawOutputParams[i].value;
return true;
}
}
return false;
}
}; //异步操作的输入操作
this.inputParams = inputParams;
//最终异步操作上下文结束时的输出参数(一般是一个操作的输出参数,all条件的输出参数比较特殊,是一个数组,每一个元素对应每一个子Workitem的输出参数)
this.outputParams = null;
this.setItemsCount = function(itemCount){
_itemCount = itemCount;
};
//测试_rawOutputParams对于key条件是否通过
this.testCondition = function(key){
return _condition[key]();
};
//索引为index的子Workitem完成时会调用这个方法
this.end = function(output, index){
_rawOutputParams[index] = {
value: output
};
if(endCallback){
endCallback(output);
}
};
//生成一个子Workitem执行时的上下文环境
this.getWorkItemContext = function(index){
//这个是子Item的上下文对象(就是this)
return {
//传递给上下文的参数
param: _this.inputParams,
//调用end方法告知异步操作的完成
end: function(output){
_this.end(output, index);
}
};
};
}; //Task的状态
var TaskStatus = {
//未开始
pending: 0,
//正在进行
doing: 1,
//已完成
finished: 2
}; //不定义具体形参,直接使用arguments
window.Task = function(){
var _status = TaskStatus.pending;
var _wItemQueue = [], _currentItem, _currentContext;
var _eventManager = new EventManager();
var _output;
//初始化一个WorkItem,并添加到执行队列中
var _initWorkItem = function(args){
var arrayArgs = [];
for(var i=0; i<args.length; i++){
arrayArgs[i] = args[i];
}
var item = new WorkItem(arrayArgs);
_wItemQueue.push(item);
return item;
};
//设置item的先决条件
var _setItemCondition = function(item, condition){
if(condition){
item.condition = condition;
}
};
//试着执行下一个异步操作,如果这个操作满足他的先决条件,那就执行;如果已经式最后一个异步操作了,就触发完成事件
var _tryDoNextItem = function(output){
var next = _getCurNextItem();
if(next){
if(_currentContext.testCondition(next.condition)){
_currentItem = next;
_doCurrentItem();
}
}
else{
_status = TaskStatus.finished;
_output = output;
_eventManager.trigger("finish", output);
}
};
//执行当前的Workitem
var _doCurrentItem = function(contextParam){
if(contextParam) {
_currentContext = new Context(_tryDoNextItem, contextParam);
}
else{
if(_currentContext){
_currentContext = new Context(_tryDoNextItem, _currentContext.outputParams);
}
else{
_currentContext = new Context(_tryDoNextItem);
}
}
_currentItem.start(_currentContext);
};
//获取下一个异步操作,如果已经是最后一个了返回undefined
var _getCurNextItem = function(){
var i=0;
for(; i<_wItemQueue.length; i++){
if(_currentItem == _wItemQueue[i]){
break;
}
}
return _wItemQueue[i + 1];
};
_currentItem = _initWorkItem(arguments); //获取Task当前状态
this.getStatus = function(){
return _status;
};
//获取Task完成时的输出参数
this.getOutput = function(){
return _output;
};
//注册完成事件
this.finished = function(callback){
if(callback){
_eventManager.addHandler("finish", callback);
}
};
//任务开始(把do改成start是因为在ie下有问题,ie会把do当做保留字)
//contextParam是一个上下文参数,可以在异步方法中通过this.Param引用
this.start = function(contextParam){
if(_status == TaskStatus.pending){
_status = TaskStatus.doing;
_doCurrentItem(contextParam);
}
return this;
};
this.then = function(){
var workItem = _initWorkItem(arguments);
_setItemCondition(workItem, 'then');
return this;
};
this.all = function(){
var workItem = _initWorkItem(arguments);
_setItemCondition(workItem, 'all');
return this;
};
this.any = function(){
var workItem = _initWorkItem(arguments);
_setItemCondition(workItem, 'any');
return this;
};
};
})(); var taskExp_1 = new Task(readFile, ["aa.txt"]).then(resolveFile, ["/service/fileResolve.ashx?file=aa.txt"]);
var taskExp_2 = new Task(readFile, ["bb.txt"]).then(resolveFile, ["/service/fileResolve.ashx?file=bb.txt"]);
var taskExp_3 = new Task(taskExp_1, taskExp_2).all(writeFile, ["cc.txt"]).then(sendMail).start(); </script>
</body>
</html>
一步一步实现基于Task的Promise库(二)all和any方法的设计和实现的更多相关文章
- 一步一步实现基于Task的Promise库(三)waitFor方法的设计
在上一篇中我们已经完成了Task.js里面的all和any方法,已经可以完美的解决大部分需求,我们再来看一个需求: 我们要先读取aa.txt的内容,然后去后台解析,同时由用户指定一个文件,也要读取解析 ...
- 一步一步实现基于Task的Promise库(五)waitFor和waitForAny的实现
在实现waitFor方法之前,我们先要搞明白下面这些问题: 1. waitFor方法的形参有限制吗? 没有!如果形参是Task类型,不应该启动Task,如果是function类型,会执行方法.所以wa ...
- 一步一步实现基于Task的Promise库(四)无参数的WorkItem
接着上一篇我直接给出代码,现在支持了new Task(), then(), all(), any() 这些不传参的调用方式. (function(){ var isFunction = functio ...
- 一步一步实现基于Task的Promise库(一)Promise的基本实现
如果我们现在有一个需求,大概是先读取一个文件的内容,再把得到的内容传给后台去解析,最后把解析后的结果再保存到那个文件,按照最原始的做法代码就是下面这个样子的: //读取文件的原始内容 var read ...
- 实现基于Task的异步模式
返回该系列目录<基于Task的异步模式--全面介绍> 生成方法 编译器生成 在.NET Framework 4.5中,C#编译器实现了TAP.任何标有async关键字的方法都是异步方法,编 ...
- 通过Dapr实现一个简单的基于.net的微服务电商系统(五)——一步一步教你如何撸Dapr之状态管理
状态管理和上一章的订阅发布都算是Dapr相较于其他服务网格框架来讲提供的比较特异性的内容,今天我们来讲讲状态管理. 目录:一.通过Dapr实现一个简单的基于.net的微服务电商系统 二.通过Dapr实 ...
- 通过Dapr实现一个简单的基于.net的微服务电商系统(六)——一步一步教你如何撸Dapr之Actor服务
我个人认为Actor应该是Dapr里比较重头的部分也是Dapr一直在讲的所谓"stateful applications"真正具体的一个实现(个人认为),上一章讲到有状态服务可能很 ...
- 通过Dapr实现一个简单的基于.net的微服务电商系统(七)——一步一步教你如何撸Dapr之服务限流
在一般的互联网应用中限流是一个比较常见的场景,也有很多常见的方式可以实现对应用的限流比如通过令牌桶通过滑动窗口等等方式都可以实现,也可以在整个请求流程中进行限流比如客户端限流就是在客户端通过随机数直接 ...
- 通过Dapr实现一个简单的基于.net的微服务电商系统(九)——一步一步教你如何撸Dapr之OAuth2授权
Oauth2授权,熟悉微信开发的同学对这个东西应该不陌生吧.当我们的应用系统需要集成第三方授权时一般都会做oauth集成,今天就来看看在Dapr的语境下我们如何仅通过配置无需修改应用程序的方式让第三方 ...
随机推荐
- 关于在同一母版页中使用多个CSS文件的解决方案
原文:关于在同一母版页中使用多个CSS文件的解决方案 以前都用.NET1.1没遇到这问题,现在换了2.0开始学着使用母版,结果就遇到了这个问题,在百度上一搜索才发现有不少人提出这个问题,但没找到好的解 ...
- 错误 4 自定义工具错误: 无法生成服务引用“DepartMentService”的代码。请检查其他错
原文:错误 4 自定义工具错误: 无法生成服务引用"DepartMentService"的代码.请检查其他错 问题: 错误 4 自定义工具错误: 无法生成服务引用" ...
- JS的parent对象
top: 该变更永远指分割窗口最高层次的浏览器窗口.如果计划从分割窗口的最高层次开始执行命令,就可以用top变量. parent: 该变量指的是包含当前分割窗口的父窗口.如果在一个窗口内有分割窗口,而 ...
- Oracle 11g password过期被锁定报道 ORA-28000 the account is locked
一.触发这个错误的原因及相关因素 是因为oracle11g中默认在default概要文件里设置了"PASSWORD_LIFE_TIME=180天"所导致.在Oracle 11g中是 ...
- app后端设计(0)--总文件夹
原文:http://blog.csdn.net/newjueqi/article/details/19003775 做了接近两年app相关的系统架构,api设计,先后在两个创业公司中工作,经历过手机网 ...
- java中文件的相对路径以及jar中文件的读取
Java中File类的构造函数需要我们传入一个pathname,当我们传入以"/"开头的pathname表示绝对路径,其他均表示相对路径. 一:绝对路径名:是完整的路径名,不需要任 ...
- 将程序添加到右键菜单和图标(以记事本、UltraEdit为例)
原文:将程序添加到右键菜单(以记事本.UltraEdit为例) 如何将程序加入右键菜单,这里分别以记事本.UltraEdit为例! 以记事本程序为例: 1. 在运行中输入regedit,打开注册表,找 ...
- [Unity3D]Unity3D游戏开发Android内嵌视图Unity查看
---------------------------------------------------------------------------------------------------- ...
- [ACM] hdu 1671 Phone List (特里)
Phone List Problem Description Given a list of phone numbers, determine if it is consistent in the s ...
- MVC验证03-自定义验证规则、禁止输入某些值
原文:MVC验证03-自定义验证规则.禁止输入某些值 本文继续体验自定义验证规则,需求是禁止输入某些值.本文与前2篇相关,请参考:MVC验证01-基础.远程验证 MVC验证02-自定义验证规则.邮 ...