一)函数式编程基础

二)异步编程的优势和难点

  2.1 优势

  2.2 难点

    2.2.1 异常处理

    2.2.2 函数嵌套过深

    2.2.3 阻塞

    2.2.4 多线程编程

    2.2.5 异步转同步

三)异步编程解决方案

  3.1 事件发布/订阅模式

    3.1.1 解决雪崩问题

    3.1.2 多异步之间的协作方案

  3.2 Promise/Deferred模式

    3.2.1 Promises/A简介

    3.2.2 支持序列化的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  难点

    2.2.1 异常处理  在实现异步I/O的过程中主要包含两个阶段 提交请求和处理结果 我们在第一个阶段提交请求后立即返回,错误有可能出现在处理结果这个阶段,所以当我们对第一个阶段进行异常处理的时候发挥不了作用 Node的解决方案是将异常作为回调函数的第一个实参传回,如果为空就代表异步调用没有异常,在编写异步方法的时候 要遵守以下的两个原则

  1. 必须执行调用者传入的回调函数
  2. 正确传递回异常供调用者判断
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较少,对于异步的调用,通过良好的流程控制,还是能够将流程梳理成顺序式的

三)异步编程解决方案 现在主要有三种典型的异步编程解决方案

  • 事件发布/订阅模式
  • Promise/Deferred模式
  • 流程控制库(流程控制库这部分介绍性的东西偏多 我没有整理)

  3.1 事件发布/订阅模式 发布/订阅模式被广泛的应用于异步编程,它通过将回调函数事件化的执行,这样就使得事件与具体的逻辑解耦和关联.在进行组件的设计的时候,通过事件的方式将自定义的部分通过事件的方式暴露给外部.并且事件模式也提供了钩子机制,利用钩子我们能导出内部数据或状态给外部的调用者

  • Node中当设置过多的监听器的时候,会收到一条警告.设计者认为这样会造成内存的泄漏,同时一系列的监听器的执行有可能过多的占用CPU,导致其他的异步调用无法执行
  • 必须对异常事件做好处理,否则会引发主线程的退出

可以通过继承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状态只能从未完成状态往失败状态或者完成态转变 并且不能逆转 完成态和失败态不能互相转化(转化后的状态无法更改)

在使用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迷你书 

  3.2.2  支持序列化的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对象(这个就是解决回调地狱的解决方案)

深入浅出node(4) 异步编程的更多相关文章

  1. node.js异步编程的几种模式

    Node.js异步编程的几种模式 以读取文件为例: 1.callback function const fs = require('fs'); //callback function fs.readF ...

  2. node.js异步编程解决方案之Promise用法

    node.js异步编程解决方案之Promise var dbBase = require('../db/db_base'); var school_info_db = require('../db/s ...

  3. 深入浅出node(3) 异步I/O

    这篇主要整理深入浅出Node.js第三章 异步I/O 一) 异步I/O的原因 二)异步I/O实现现状 2.1 异步I/O与非阻塞I/O 2.2 轮询 2.3 理想的非阻塞异步I/O 2.4 现实的异步 ...

  4. 深入理解node.js异步编程:基础篇

    ###[本文是基础内容,大神请绕道,才疏学浅,难免纰漏,请各位轻喷] ##1. 概述 目前开源社区最火热的技术当属Node.js莫属了,作为使用Javascript为主要开发语言的服务器端编程技术和平 ...

  5. 17.Node.js 回调函数--异步编程

    转自:http://www.runoob.com/nodejs/nodejs-tutorial.html Node.js 异步编程的直接体现就是回调. 异步编程依托于回调来实现,但不能说使用了回调后程 ...

  6. Node.js之异步编程

    > 文章原创于公众号:程序猿周先森.本平台不定时更新,喜欢我的文章,欢迎关注我的微信公众号. ![file](https://img2018.cnblogs.com/blog/830272/20 ...

  7. node(1) npm是什么?node的异步概念

    NPM是随同的NodeJS一起安装的包管理工具 他可以做什么? 1.可以从NPM服务器下载别人的东西使用 2.可以把自己的东西传到NPM服务器,让别人下载使用 淘宝的镜像会快一点      cnpm ...

  8. 09-Node.js学习笔记-异步编程

    同步API,异步API 同步API:只有当前API执行完成后,才能继续执行下一个API console.log('before'); console.log('after'); 异步API:当前API ...

  9. node 单线程异步非阻塞

    链接:http://www.runoob.com/nodejs/nodejs-callback.html 首先什么是单线程异步非阻塞? 单线程的意思整个程序从头到尾但是运用一个线程,程序是从上往下执行 ...

随机推荐

  1. jQuery 杂项方法

    jQuery 杂项方法 方法 描述 data() 向被选元素附加数据,或者从被选元素获取数据 each() 为每个匹配元素执行函数 get() 获取由选择器指定的 DOM 元素 index() 从匹配 ...

  2. wireshark添加用户执行

    默认情况下,访问网络端口需要root权限,而wireshark的只是/usr/share/dumpcap的一个UI,/usr/share/dumpcap需要root权限,所以没法non-root用户无 ...

  3. JavaScript拖拽

    <!DOCTYPE html><html><head> <meta charset="utf-8"> <meta http-e ...

  4. JavaScript 输入自动完成插件

    作为web开发的一员,应该都不陌生,信息处理时,很多时候需要根据用户的输入实时反馈查询结果供其选择,这给了用户很好的人机交互体验,在各大门户网站上已经被使用的很成熟了,最近项目中用到此功能,网上有很多 ...

  5. 添加标签2 jquery 和JS

    TAG添加标签 做了个方法方便调用 一.JS版本 <!DOCTYPE html> <html lang="en"> <head> <met ...

  6. 关于手机端CSS Sprite图标定位的一些领悟

    今天在某个群里面闲逛,看见一个童鞋分享了一个携程的移动端的页面.地址这里我也分享下吧:http://m.ctrip.com/html5/在手机端我都很少用雪碧图合并定位图标,用的比较多就是用字体图标来 ...

  7. 学习Swift -- 数组(Array) - 持续更新

    集合类型--数组 Array是Swift中的一种集合类型:数组,数组是使用有序列表储存同一类型的多个值,与OC的NSArray的最大不同是,Swift的数组是值类型,OC的数组是引用类型 声明数组的方 ...

  8. IIs工作原理

    http://www.cnblogs.com/szhy222/archive/2008/07/14/1242576.html 问题: HTTP.SYS 的内置驱动程序 IIS 工作者进程

  9. Contest 20141027 总结

    这次考试主要问题出在第一题,由于考试期间没有看清题意,少看了一句 “a=A/1e9" 导致在考试结束最后5分钟发现时修改过于匆忙,改出问题了.另外,这道题同时告诉我long double 在 ...

  10. [CF Round #294 div2] D. A and B and Interesting Substrings 【Map】

    题目链接:D. A and B and Interesting Substrings 题目大意 给定26个小写字母的权值,一共26个整数(有正有负). 给定一个小写字母组成的字符串(长度10^5),求 ...