一)函数式编程基础javascript
2.1 优点java
2.2 难点git
2.2.1 异常处理github
2.2.2 函数嵌套过深web
2.2.3 阻塞数据库
2.2.5 异步转同步promise
三)异步编程解决方案多线程
1.1 高阶函数 高阶函数式能够接受函数做为参数或者返回值的函数,这样在编写程序的时候就增长了灵活性,还能造成一种后续传递风格的结果接受方式,将业务从返回值转移到回到函数中
function test(x) { return x + 1; } function test2(x) { return x + 2; } function func(x,bar) { return bar(x); } console.log(func(2,test)); 3 console.log(func(2,test2)); 4
1.2 偏函数 偏函数指建立一个调用另一个部分(参数或者变量已经预置的函数)的函数
var toString = Object.prototype.toString; var isType = function(type) { return function(obj) { return toString.call(obj) == '[object ' + type + ']' } } //这里咱们经过预置了type参数 来实现了偏函数
2.1 优点 Node中异步编程的最大优点是基于时间驱动的非阻塞的I/O模型,这样就能使CPU和I/O互相不依赖,更好的利用资源,从而达到并行的目的(主要是减小对CPU的占用)
2.2.1 异常处理 在实现异步I/O的过程当中主要包含两个阶段 提交请求和处理结果 咱们在第一个阶段提交请求后当即返回,错误有可能出如今处理结果这个阶段,因此当咱们对第一个阶段进行异常处理的时候发挥不了做用 Node的解决方案是将异常做为回调函数的第一个实参传回,若是为空就表明异步调用没有异常,在编写异步方法的时候 要遵照如下的两个原则
var async = function(callback) { //一些操做 获取一些数据或者数据处理等 var results = something; if(error) { return callback(error); } callback(null,results); }
2.2.2 函数嵌套过深 在咱们获取资源的时候,资源之间是没有依赖关系的,在进行结果处理的时候却须要三者,这样就会形成函数的嵌套
2.2.3 阻塞代码 对于javascript编程来讲,是不存在阻塞代码的,Node中单线程的缘由,线程须要运行事件循环的调度,长时间的占用主线程会破坏事件循环,解决方案是统一业务逻辑,使用setTimeout()来完成相似的效果
2.2.4 多线程编程 相似于前端提出的web workers,Node中提出了child_process,cluster是更深层的多线程方式
2.2.5 异步转同步 Node中同步的API较少,对于异步的调用,经过良好的流程控制,仍是可以将流程梳理成顺序式的
3.1 事件发布/订阅模式 发布/订阅模式被普遍的应用于异步编程,它经过将回调函数事件化的执行,这样就使得事件与具体的逻辑解耦和关联.在进行组件的设计的时候,经过事件的方式将自定义的部分经过事件的方式暴露给外部.而且事件模式也提供了钩子机制,利用钩子咱们能导出内部数据或状态给外部的调用者
能够经过继承events模块来实现发布/订阅模式来解决业务中的问题
var events = require("events"); var util = require("util"); function Stream() { events.EventEmitter.call(this); } util.inherits(Stream,events.EventEmitter);
3.1.1 解决雪崩问题 雪崩问题是忽然间大量相同的操做进行访问的时候,致使同时进行相同的查询或者操做,数据库没法同时承受如此大的查询请求,进而影响网站的整体速度
一种比较简单的方案就是使用状态锁
var status = "ready"; var select = function(callback){ if(status === "ready") { status = "pending"; db.select("SQL",function(err,results){ status = "ready"; callback(results); }); } } // 这样当同时进行屡次调用的时候,只有首次的调用是有数据服务的 这样的方式并不合理
结合状态锁和发布/订阅模式来解决雪崩问题
var events = require("events"); var proxy = new events.EventEmitter(); var status = "ready"; var select = function(callback){ proxy.once("selected",callback); if(status === "ready") { status = "pending"; db.select("SQL",function(error,results){ proxy.emit("selected",results); status = "ready"; }); } }//在这种模式下一样的请求以后执行一次,后续的请求会被加入到事件的队列中,当数据可用时,每一个请求的回调函数都会被执行一次
3.1.2 多异步之间的协做方案 可使用发布/订阅模式来解决嵌套过深和梳理业务逻辑 哨兵变量指用于检测次数的变量 咱们可使用偏函数和哨兵变量的模式来梳理异步做业中多对一的场景
var after = function(times,callback){ var counts = 0,results = {}; return function(key,value){ results[key] = value; count++; if(count === times) { callback(results); } } } //这样在完成times调用的时候,才会调用回调函数 而且会把以前的结果传入 还有朴大写的EventProxy模块也是经过补充发布订阅模式,来协同多异步操做
3.2 Promise/Deferred模式 Promise/Deferred模式它是一种先执行异步延时传递处理的模式,它能弥补发布/订阅模式的不足(必须预先设定好分支),Promise/Deferred模式同时能必定上解决函数调用嵌套的问题,方便咱们更好的理解业务逻辑 在ES6中已经提供了对Promise的支持 简单的理解Promise/Deferred模式 then()方法就是把回调函数存放起来 而后经过Deferred(延时)对象在适当的时候去调用保存起来的方法
3.2.1 Promises/A简介 Promises/A对单个异步操做进行了抽象的定义
在使用Promise的时候,只须要在Promise的then()方法中传递相应的回调函数便可,它就会在异步操做处于完成或者失败状态的时候调用对应的函数而且将结果做为参数传递进去,then()方法只接受function对象而且执行完then()方法后继续返回Promise()对象来实现链式调用
var util = require("util"); var events = require("events"); var Promise = function() { events.EventEmitter.call(this); } util.inherits(Promise,event.EventEmitter); Promise.prototype.then = function(fulfilledHandler,errorHandler,progressHandler){ if(typeof fulfilledHandler === 'function') { this.once('success',fulfilledHandler); } if(typeof errorHandler === 'function') { this.once('error',errorHandler); } if(typeof progressHandler === 'function') { this.once('progress',progressHandler); } return this; }; var Deferred = function(){ this.state = 'unfulfilled'; this.promise = new Promise(); } Deferred.prototype.resolve = function(obj) { this.state = 'fulfilled'; this.promise.emit('success',obj); } Deferred.prototype.reject = function(obj) { this.state = 'failed'; this.promise.emit('failed',obj); } Deferred.prototype.progress = function(data) { this.promise.emit('progress',data); } //咱们在实际操做的时候,就是经过对Deferred进行封装来实现Promise\Deferred模式 也就是在异步操做成功的时候调用Deferred对象的resolve方法,在失败的时候调用reject方法
下面咱们封装一个基本的数据请求
var promisify = function(res) {
var deferred = new Deferred();
var results = '';
res.on('data',function(chunk){
results += chunk;
deferred.progress(chunk);
})
res.on('end',function(){
deferred.resolve(results);
})
res.on('error',function(err){
deferred.reject(err);
})
return deferred.promise;
}
推荐这本 promise迷你书
var Promise = function(){ this.isPromise = true; this.queue = []; } Promise.prototype.then = function(fulfilledHandler,errorHandler,progressHandler){ var handler = {}; if(typeof fulfilledHandler === 'function') { handler.fulfilled = fulfilledHandler; } if(typeof errorHandler === 'function') { handler.error = errorHandler; } this.queue.push(handler); return this; } var Deferred = function() { this.promise = new Promise(); } Deferred.prototype.resolve = function(obj) { var promise = this.promise; var handler; while((handler = promise.queue.shift())) { if(handler && handler.fulfilled) { var ret = handler.fulfilled(obj); if(ret && ret.isPromise) { ret.queue = promise.queue; this.promise = ret; return; } } } } Deferred.prototype.reject = function(err) { var promise = this.promise; var hanlder; while((hanlder = promise.queue.shift())) { if(handler && hanlder.error) { var ret = hanlder.error(err); if(ret && ret.isPromise) { ret.queue = promise.queue; this.promise = ret; return; } } } }//上面的的代码进全部的回调函数保存为对象存储在队列中.promise完成是依次的执行回调,若是返回新的promise对象,就将当前deferred对象的promise对象的promise设置为新的promise对象(这个就是解决回调地狱的解决方案)