您的位置:首页 > Web前端 > Node.js

Nodejs 回调函数中的坑以及中间件的用法

2016-03-30 21:41 549 查看
在用Nodejs + express 开发后台的过程中,最令人头疼的就是到处存在的回调函数了。不管是http请求,还是数据库请求,都是强制回调的。这是由js本身的特性导致的。

所谓回调,就是指假设A将任务分配给B去运行。之后A就可以把这个任务放在一边,去执行其他任务。当B执行完以后,将结果告诉A,A会捡起之前没完成的任务继续做。有点类似于中断的模式。这样一来,虽然程序的性能得以保证,但是许多问题也是随之而来。

第一个问题是异常捕获变得更加困难。如果在A所执行程序的两端加上try…catch,而在B执行回调过程中发生了错误,A是不会报错的。这点要特别注意。所以要特别注意对返回函数中异常信息的处理。这点是比较令人困扰的。举例如下

典型的回调函数写法

try{
// 程序A
req.models.vote.create(record, funtion(err, result){
// 回调函数
if (err) {
// blablabla...
// 对B执行结果的异常处理
}
// A需要完成的接下来的任务
})
}catch(err){
// A的异常处理
}


上面这段程序用来在mydql中创建新数据行。当然其他回调的用法也差不多。可以看到,虽然A有try catch对,但是在回调过程中,如果B发生了错误,那么A是不会报错的。只是在回调函数funtion(err, result)中,有相关的错误信息。

第二个问题是回调函数使得代码的执行顺序不再线性,有可能会造成逻辑混乱。还是拿上面的程序举例,稍微修改下

try{
// A的第一块代码
req.models.vote.create(record, funtion(err, result){
// 回调函数
if (err) {
// blablabla...
// 对B执行结果的异常处理
}
// A需要完成的接下来的任务
})

// A的第二块代码
// blablabla...

// A的第三块代码
// blablabla...
}catch(err){
// A的异常处理
}


上面这段程序,A在执行第一块代码时,把一个任务交给B去处理。关键是,之后A会按顺序执行下面的代码,第二块代码,第三块代码… 以此类推。那么问题来了,回调函数有可能先于代码块2被执行,也有可能晚于代码块2.那么如果代码块2和回调函数调用了同一个资源,就很有可能会报错。我在写的时候就碰到过一个,使得对一个http请求重复返回了2次内容,但是一个http只能有一个返回呀,于是就报错了。

解决的办法,就是将A后续的代码都放进回调函数中。或者对于两个并行的逻辑,尽量使用if-else逻辑,而不要用if-return逻辑。如下所示

// ---------------------------------------------------
// 【将A后续代码放入回调函数中】
req.models.vote.create(record, funtion(err, result){
// 回调函数
if (err) {
// blablabla...
// 对B执行结果的异常处理
}
// A的第二块代码
// A的第三块代码
// ..
})
// ---------------------------------------------------
// 【尽量使用if-else逻辑】
// 这样就只能执行A,B中的一个,而不会同时执行,导致报错
if ( logic 1 ){
// 回调函数A
return
}
else {
// 回调函数B
return
}
// ---------------------------------------------------
// 【不要使用下面这种方法】
// 看似能够精简代码,实则有可能会导致A和B都执行
if ( logic 1 ){
// 回调函数 A
return
}
//回调函数 B
return


第三个问题,频繁使用回调会使得代码逻辑一片混乱,嵌套过深,可读性和可维护性都非常糟糕。被称作”callback hell”。比如有一个业务逻辑,需要读取一次网页,然后读取若干次数据库。。。于是你的函数就变成这样了。。。

req.models.vote.create(record, funtion(err, result){
// 回调函数B
if (err) {
// blablabla...
// 对B执行结果的异常处理
}
req.models.xxx.find(..,function(err,result){
// 回调函数C
if (err){
// 对C执行结果的异常处理
}
req.models.xxx.find(..,function(err,result){
// 回调函数D
if (err) {..}
})
})
})


如上所示,不停的嵌套嵌套嵌套。。。。画面太美不敢看。。这时候为了让代码的逻辑更加清晰,需要使用中间件。在express中,是可以使用中间件来处理业务逻辑的。(其他的我不太清楚,应该也是有的)。所谓的中间件有点类似于linux中的管道,前一个函数执行完以后,如果没有返回,就由下一个函数处理。如下所示。(以express应用为例)

// 【router.js】
var test = require('./test.js')
app.post('/',test.a,test.b) ;

// 【test.js】
module.exports.a = function(req,res,next){ //必须有这三个变量
req.models.vote.create(record, funtion(err, result){
// 回调函数B
if (err) {
// blablabla...
// 对B执行结果的异常处理
return res ; //返回http请求,不进入下一单元
}
// 一些非异步的操作..
next() ;
// 如果后面还有中间件,则next表示传给下个中间件
}
} ;
module.exports.b = function(req,res,next){
req.models.vote.create(record, funtion(err, result){
// 回调函数B
if (err) {
// blablabla...
// 对B执行结果的异常处理
return res ;
}
// 一些非异步的操作..
return res ;
// 如果是最后一个中间件,则不能有next函数,不然会导致无响应
}
} ;


每个中间件的参数都是req,res,next. req中有一些请求的必要信息,以及有可能有上一个中间件的执行结果,res表示要返回给客户端的内容,next表示调用下一个中间件,只能在非结尾的中间件中使用
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: