一步一步实现基于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的语境下我们如何仅通过配置无需修改应用程序的方式让第三方 ...
 
随机推荐
- ABP应用层——参数有效性验证
			
ABP应用层——参数有效性验证 基于DDD的现代ASP.NET开发框架--ABP系列之17.ABP应用层——参数有效性验证 ABP是“ASP.NET Boilerplate Project (ASP. ...
 - ubuntu14.04通过将语音,耳机没有声音
			
真正使用linux每天都有新的发现啊,截至今天上午有一台电脑,听几首歌.后插上耳机中没有声音.我认为这是一个糟糕的耳机,把插在手机耳机,我发现了一个语音耳机,很奇怪,我喜欢计算机接口是不是有问题,是在 ...
 - 网络资源(7) - JAX-WS视频
			
2014_08_25 http://v.youku.com/v_show/id_XNjMzNDcyMTk2.html 基于JAX-WS编程模型的WebService 1. @WebService注释类 ...
 - Node.js v0.10.31API手工-DNS
			
原版的API品种,这是从以前的翻译和翻译风格不同 Node.js v0.10.31API手冊-文件夹 DNS 使用 require('dns') 引入此模块. dns 模块中的全部方法都使用了 C-A ...
 - 至linuxNIC添加多个IP
			
由于工作的需要,只是有一个2以太网端口server构造3个月IP.制linux. 整理如以下的现在的过程,有离开后,,学生们也将不能够引用. IP1:10.110.97.68 IP2:10.115.2 ...
 - oracle_powerdesinger逆向工程 , PDM 文件 注释到name的完美解决方案 comment2name
			
1. 从oracle 到 PDM文件 逆向工程中 ,需要注意 去掉“” ,这个百度下很多帖子,用于去掉引号 2. 从注释copy到name运行脚本会有个问题就是 ,有些注释太长,不美观 解决方案, ...
 - TS流文件
			
简单介绍编辑 随着从HDTV录制的高清节目在网上的流传,烧友们对TS这个名词大概已经不陌生了.但随之而来就是怎样播放.怎样加入字幕等等的一系列问题.本文将重点介绍一下这方面的应用操作. 先来简要介绍一 ...
 - 你知道OneNote的OCR功能吗?office lens为其增大威力,中文也识别
			
原文:[原创]你知道OneNote的OCR功能吗?office lens为其增大威力,中文也识别 OneNote提供了强大的从图片中取出文字的功能,大家只要装上了桌面版OneNote(本人用的2013 ...
 - 通过EA导入数据库存在表结构并生成文档
			
通过EA导入数据库存在表结构并生成文档 慕课网,程序员升职加薪神器,点击免费学习 目录[-] 导入数据源,表结构 生成表结构的文档 Enterprise Architect 是超级强大项目管理功能 ...
 - 在WAMPSERVER下增加多版本的PHP(PHP5.3,PHP5.4,PHP5.5)支持。
			
原文:在WAMPSERVER下增加多版本的PHP(PHP5.3,PHP5.4,PHP5.5)支持. WAMPServer可以让开发者在Windows系统下快速搭建WAMP环境,它支持多版本的Apach ...