深入浅出node(4) 异步编程
一)函数式编程基础
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的解决方案是将异常作为回调函数的第一个实参传回,如果为空就代表异步调用没有异常,在编写异步方法的时候 要遵守以下的两个原则
- 必须执行调用者传入的回调函数
- 正确传递回异常供调用者判断
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) 异步编程的更多相关文章
- node.js异步编程的几种模式
Node.js异步编程的几种模式 以读取文件为例: 1.callback function const fs = require('fs'); //callback function fs.readF ...
- node.js异步编程解决方案之Promise用法
node.js异步编程解决方案之Promise var dbBase = require('../db/db_base'); var school_info_db = require('../db/s ...
- 深入浅出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 现实的异步 ...
- 深入理解node.js异步编程:基础篇
###[本文是基础内容,大神请绕道,才疏学浅,难免纰漏,请各位轻喷] ##1. 概述 目前开源社区最火热的技术当属Node.js莫属了,作为使用Javascript为主要开发语言的服务器端编程技术和平 ...
- 17.Node.js 回调函数--异步编程
转自:http://www.runoob.com/nodejs/nodejs-tutorial.html Node.js 异步编程的直接体现就是回调. 异步编程依托于回调来实现,但不能说使用了回调后程 ...
- Node.js之异步编程
> 文章原创于公众号:程序猿周先森.本平台不定时更新,喜欢我的文章,欢迎关注我的微信公众号.  npm是什么?node的异步概念
NPM是随同的NodeJS一起安装的包管理工具 他可以做什么? 1.可以从NPM服务器下载别人的东西使用 2.可以把自己的东西传到NPM服务器,让别人下载使用 淘宝的镜像会快一点 cnpm ...
- 09-Node.js学习笔记-异步编程
同步API,异步API 同步API:只有当前API执行完成后,才能继续执行下一个API console.log('before'); console.log('after'); 异步API:当前API ...
- node 单线程异步非阻塞
链接:http://www.runoob.com/nodejs/nodejs-callback.html 首先什么是单线程异步非阻塞? 单线程的意思整个程序从头到尾但是运用一个线程,程序是从上往下执行 ...
随机推荐
- c#中用DirectShow实现媒体播放器的核心(1) DirectShow简介
用.net做多媒体开发的似乎不多,所以网上资源也少,看的人更少.不过我的博客上居然还有几位在等新文章的人,有点出乎我的意料了.目前我已不再从事多媒体相关的工作,加入新公司至今都忙到吐血,再加上害怕水平 ...
- 使用ArrayList对大小写字母的随机打印
从a~z以及A~Z随机生成一个字母并打印:打印全部的字母 package com.liaojianya.chapter1; import java.util.ArrayList; /** * This ...
- map函数(转)
STL中map用法详解 说明:如果你具备一定的C++ template知识,即使你没有接触过STL,这个文章你也应该可能较轻易的看懂.本人水平有限,不当之处,望大家辅正. 一.Map概述 Map是ST ...
- matlab结构体形式保存数据生成.mat文件< 转>
2015年 参加天池大数据竞赛 为了建立模型,打算基于matlab使用Random Forest Algorithm的工具包 该工具包我在此分享给大家,http://yunpan.cn/cVX ...
- 如何在一个网站或者一个页面,去书写你的JS代码
// JavaScript Document //如何在一个网站或者一个页面,去书写你的JS代码: //1.js的分层(功能) : jquery(tools) 组件(ui) 应用(app), mvc( ...
- .net 对配置文件内容的操作
配置文件分为两种 :一种是winform应用程序的配置文件, 一种是web的配置文件. 两种配置文件最大的区别是web的配置文件更新之后会时时更新, 应用程序的配置文件不会实时更新. 更新应用程序的配 ...
- 百度编辑器ueditor 使用
ueditor 百度开源的一个 编辑器 ,支持api.扩展,demo丰富.推荐下 以前写 编辑 词典的使用 jquery-te 轻量级编辑器..当时看中了 它代码轻巧.容易改. 把他的功能改了好多. ...
- http server v0.1_http_webapp.c
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h&g ...
- 10个WordPress增强型编辑器大检阅
Wordpress之所以优秀不在于其本身,而在于无数的插件开发者和性能优化人员.使用过Wordpress的人都知道,WP本身的编辑器极其简陋,就连简单的表格编辑也没有,可能吧这篇文章将介绍一些免费的增 ...
- 这篇blog只是为了发一张图链到UOJ的博客去..
UOJ卖萌表情,萌萌哒VFK如图.