Node是首个将异步大规模带到应用层面的平台,它从内在运行机制到API的设计,无不透露出异步的气息来。数据库
在JavaScript中,函数做为一等公民,使用上很是自由,不管调用它,或者做为参数,或者做为返回值都可。编程
函数式编程是JavaScript异步编程的基础。promise
高阶函数把函数做为参数,或是将函数做为返回值的函数。缓存
function Night(x){
return function (){
return x;
}
}
复制代码
事件的处理方式正是基于高阶函数的特性的灵活性来完成的。bash
ECMAScript5提供的高阶函数:网络
偏函数用法是指建立一个调用另一个部分-参数或者变量已经预置的函数-的函数的用法。多线程
例子:并发
var toString = Object.prototype.toString;
var isString = function (obj) {
return toString.call(obj) == '[object string]';
};
var isFunction = function (obj) {
return toString.call(obj) == '[object function]';
};
复制代码
例子改进(工厂模式):异步
var isType = function (type) {
return function (obj) {
retirn toString.call(obj) == '[object' + type + ']';
}
};
var isString = isType('String');
var isFunvtion = isType('Funvtion');
复制代码
这种经过指定部分参数来产生一个新的定制的形式就是偏函数。async
Node带来的最大特性是基于事件驱动的非阻塞I/O模型。非阻塞I/O可使CPU与I/O并不相互依赖等待,让资源更好的利用。对于网络应用而言,并行带来的想象空间巨大。延展而开是分布式和云。
因为事件循环模型须要应对海量的请求,海量请求同时做用在单线程上,就须要防止任何一个计算耗费过多的CPU时间片。至因而计算密集型,仍是I/O密集型,只要计算不要影响异步I/O的调度,那就不构成问题。
难点:
异常处理;
编写异步方法遵循的原则:
函数嵌套过深;
阻塞代码;
多线程编程;
使用Web Workers,利用消息机制是合理使用多核CPU的理想模型。
Web Workers可以解决利用CPU和减小阻塞UI渲染,可是不能解决UI渲染的效率问题。
异步转同步;
事件监听器模式是一种普遍用于异步编程的模式,是回调函数的事件化,又称发布/订阅模式。
事件发布/订阅模式:
// 订阅
emitter.on("event1",function(message){
console(message);
})
// 发布
emitter.emit("event1","I am message!");
复制代码
订阅事件就是个高阶函数的应用。事件发布/订阅模式能够实现一个事件与多个回调函数的关联,这些回调函数又称为事件侦听器。
经过emit()发布事件后,消息会当即传递给当前事件的全部侦听器执行。侦听器能够很灵活地添加和删除,使得事件和具体处理逻辑之间能够很轻松关联和解耦。
从另外一种角度看,事件侦听器模式也是一种钩子(hook)机制,利用钩子导出内部数据或状态给外部的调用者。
Node对事件发布/订阅的机制的额外处理:
若是一个事件添加了超过10个侦听器,将会获得一个警告;
为了处理异常,EventEmitter对象对error事件进行了特殊对待;
Node中Stream对象继承EventEmitter的例子:
var events = require('events');
function Stream (){
events.EventEmitter.call(this);
}
util.inherits(Stream,events.EventEmitter);
复制代码
once():侦听器只能执行一次,在执行以后就会将它与事件的关联移除。
采用once()解决雪崩问题。
雪崩问题:
在高访问量、大并发量的状况下缓存失效的情景,此时大量的请求同时涌入数据库中,数据库没法同时承受如此大的查询请求,进而往前影响网站的总体的响应速度。
一条数据库查询语句的调用:
var select = function (callback) {
db.select("SQL",function (results) {
callback(results);
});
};
复制代码
通常而言,事件与侦听器的关系是一对多,但在异步编程中,也会出现事件与侦听器的关系是多对一的状况,也就是说一个业务逻辑可能依赖两个经过回调或事件传递的结果。回调嵌套过深的缘由就是如此。
因为多个异步场景中回调函数的执行并不能保证顺序,且回调函数之间没有任何交集,因此须要借助一个第三方函数和第三方变量来处理异步协做的结果。这个用于监测次数的变量叫作哨兵变量。
EventProxy的原理
EventProxy来自于Backbone的事件模块,Backbone的事件模块是Model、View模块的基础功能。
EventProxy将all当作一个事件流的拦截层,在其中注入一些业务来处理单一事件没法解决的异步处理问题。
EventProxy的异常处理
EventProxy提供了fail()和done()这两个实例方法来优化异常处理,使得开发者将精力关注在业务实现,而不是在异常捕获上。
使用事件的方式时,执行流程须要被预先设定。即使是分支,也须要预先设定,这是由发布/订阅模式的运行机制所决定的。
是否有一种先执行异步调用,延迟传递处理的方式呢?
答案是Promise/Deferred模式。
一个Promise对象只要具有then()方法便可。
对于then()方法的要求:
then()方法所作的事情是将回调函数给存储起来,为了完成整个流程,还须要触发执行这些回调函数的地方,实现这些功能的对象一般被称为Deferred,即延迟对象。
Promise和Dererred的差异:
Promise做用于外部,经过then()方法暴露给外部已添加自定义逻辑;
Dererred做用于内部,用于维护异步模型的状态;
Promise是高级接口,事件是低级 接口。低级接口能够构建更多更复杂的场景,高级接口一旦定义。不太容易变化,再也不有低级接口的灵活性。但对于解决典型问题很是有效。
Promise经过封装异步调用,实现了正向用例和反向用例的分离以及逻辑处理延迟。
Promise须要封装,可是强大,具有很强的侵入性,纯粹的函数则较为轻量,但功能相对较弱。
Promise中的多异步协做
相似于EventProxy。
Promise的进阶知识
在API的暴露上,Promise模式比原始的事件侦听和触发略为优美,缺陷是须要为不一样的场景封装不一样的API,没有直接的原生事件那么灵活。
Promise的秘诀其实在于对队列的操做。
支持序列执行的Promise
理想的编程体验是前一个的调用的结果做为下一个调用的开始,就是所谓的链式调用。
要让Promise支持链式执行的步骤:
将APIPromise化
尾触发与Next
尾触发:
须要手工调用才能持续执行后续调用的。
常见的关键词是Next.
尾触发目前应用最多的地方是Connect的中间件。
中间件最简单的例子:
function (req,res,next) {
//中间件
}
复制代码
每一个中间件传递请求对象、响应对象和尾触发函数,经过队列造成一个处理流。
中间件机制使得在 处理网络请求时,能够像面向切面编程同样进行过滤、验证、日志等功能,而不与具体业务逻辑产生关联,以至产生耦合。
原始的next()方法较为复杂,简化和的原理十分简单,取出队列的中间件并执行,同时传入当前方法以实现递归调用,达到持续触发的目的。
async
Step
比async更轻量。
Step接收任意数量的任务,全部的任务都将会串行依次执行。
Step与事件模式、Promise、async都不一样的一点在于Step用到了this关键字。事实上,它是Step内部的一个next()方法,将异步的调用的结果传递给下一个任务做为参数,并调用执行。
wind
wind为JavaScript语言提供了一个monadic扩展,可以显著提升一些常见场景下的异步编程体验。
异步I/O与同步I/O的显著差距:
同步I/O由于每一个I/O都是彼此阻塞的,在循坏体中,老是一个接着一个调用,不会出现耗用文件描述符太多的状况,同时性能也是低下的;
异步I/O,虽然并发容易实现,可是因为太容易实现,依然须要控制;
bagpipe模块的解决思路:
bagpipe相似于打开了一道窗口,容许异步调用并行进行,可是严格限定上限。仅仅在调用push()时分开传递,并不对原有API有任何侵入。
async提供的处理异步调用的限制:parallelLimit()。