待补充,原文链接:https://github.com/alsotang/node-lessons/tree/master/lesson18
HTTP
Nodejs 的经典 httpServer 代码
var http = require('http');
var server = http.createServer(requestHandler);
function requestHandler(req, res) {
res.end('hello visitor!');
}
server.listen(3000);
里面的函数 requestHandler 就是所有http请求的响应函数,即所有的请求都经过这个函数的处理,是所有请求的入口函数。
通过 requestHandler 函数我们能写一些简单的 http 逻辑,比如上面的例子,所有请求都返回 hello visitor!。
然而,我们的业务逻辑不可能这么简单。例如:需要实现一个接口,要做的是当请求过来时,先判断来源的请求是否包含请求体,然后判断请求体中的id是不是在数据库中存在,最后若存在则返回true,不存在则返回false。
1. 检测请求中请求体是否存在,若存在则解析请求体;
1. 查看请求体中的id是否存在,若存在则去数据库查询;
1. 根据数据库结果返回约定的值;
我们首先想到的,抽离函数,每个逻辑一个函数,简单好实现低耦合好维护。
实现代码:
function parseBody(req, callback) {
//根据http协议从req中解析body
callback(null, body);
}
function checkIdInDatabase(body, callback) {
//根据body.id在Database中检测,返回结果
callback(null, dbResult);
}
function returnResult(dbResult, res) {
if (dbResult && dbResult.length > 0) {
res.end('true');
} else {
res.end('false')
}
}
function requestHandler(req, res) {
parseBody(req, function(err, body) {
checkIdInDatabase(body, function(err, dbResult) {
returnResult(dbResult, res);
});
});
}
上面的解决方案解决了包含三个步骤的业务问题,出现了3个 }); 还有3个 err 需要处理,上面的写法可以得达到预期效果。
然而,业务逻辑越来越复杂,会出发展成30个回调逻辑,那么就出现了30个 }); 及30个 err异常。更严重的是,到时候写代码根本看不清自己写的逻辑在30层中的哪一层,极其容易出现 多次返回 或返回地方不对等问题,这就是 回调金字塔 问题了。
大多数同学应该能想到解决回调金字塔的办法,朴灵的《深入浅出Node.js》里讲到的三种方法。下面列举了这三种方法加上ES6新增的Generator,共四种解决办法。
- EventProxy —— 事件发布订阅模式(第四课讲到)
- BlueBird —— Promise方案(第十七课讲到)
- Async —— 异步流程控制库(第五课讲到)
- Generator —— ES6原生Generator
理论上,这四种都能解决回调金字塔问题。而Connect和Express用的是 类似异步流程控制的思想 。
关于异步流程控制库下面简要介绍下,或移步@第五课。 异步流程控制库首先要求用户传入待执行的函数列表,记为funlist。流程控制库的任务是让这些函数 顺序执行 。
callback是控制顺序执行的关键,funlist里的函数每当调用callback会执行下一个funlist里的函数
我们动手实现一个类似的链式调用,其中 funlist 更名为 middlewares、callback 更名为 next,码如下:
var middlewares = [
function fun1(req, res, next) {
parseBody(req, function(err, body) {
if (err) return next(err);
req.body = body;
next();
});
},
function fun2(req, res, next) {
checkIdInDatabase(req.body.id, function(err, rows) {
if (err) return next(err);
res.dbResult = rows;
next();
});
},
function fun3(req, res, next) {
if (res.dbResult && res.dbResult.length > 0) {
res.end('true');
}
else {
res.end('false');
}
next();
}
]
function requestHandler(req, res) {
var i=0;
//由middlewares链式调用
function next(err) {
if (err) {
return res.end('error:', err.toString());
}
if (i<middlewares.length) {
middlewares[i++](req, res, next);
} else {
return ;
}
}
//触发第一个middleware
next();
}
上面用middlewares+next完成了业务逻辑的 链式调用,而middlewares里的每个函数,都是一个 中间件。
整体思路是:
- 将所有
处理逻辑函数(中间件)存储在一个list中; - 请求到达时
循环调用list中的处理逻辑函数(中间件);
Connect的实现
Connect的思想跟上面阐述的思想基本一样,先将处理逻辑存起来,然后循环调用。
Connect中主要有五个函数 PS: Connect的核心代码是200+行,建议对照源码看下面的函数介绍。
| 函数名 | 作用 |
|---|---|
| createServer | 包装httpServer形成app |
| listen | 监听端口函数 |
| use | 向middlewares里面放入业务逻辑 |
| handle | 上一章的requestHandler函数增强版 |
| call | 业务逻辑的真正执行者 |
Express
大家都知道Express是Connect的升级版。
Express不只是Connect的升级版,它还封装了很多对象来方便业务逻辑处理。Express里的Router是Connect的升级版。
Express大概可以分为几个模块
| 模块 | 描述 |
|---|---|
| router | 路由模块是Connect升级版 |
| request | 经过Express封装的req对象 |
| response | 经过Express封装的res对象 |
| application | app上面的各种默认设置 |
简要介绍一下每个模块
本文探讨Node.js中处理复杂业务逻辑时遇到的回调地狱问题,并介绍如何使用中间件和链式调用来简化代码结构。通过具体示例,文章展示了传统回调方式的局限性及其替代方案。
615

被折叠的 条评论
为什么被折叠?



