如何用nodejs 把业务逻辑写的漂亮


背景:接触nodejs 写业务代码不久。 一直是phper

这个问题可能问的不是很恰当, 因为大部分时候可能代码写的好不好跟具体语言很多时候是无关的。
仅仅提出我的看法:

  1. 太多回调。 用回调倒是没关系,但是很多代码里回调是直接写的匿名函数。 匿名函数第一眼无法通过函数名理解其作用,其次是如果这个回调的匿名函数里如果逻辑太多,不利于重用。
  2. 异步, 显然用异步写的代码,肯定理解起来要困难。
  3. 很多函数会这样写 function (params, callback){ //...} ,把所有参数放到一个对象里传进去。因为总要留个参数位置给 callback , 要多写一个变量, 就会偷懒,把其他参数当作对象传递进去。 个人不是很喜欢这种作法,会让人猜不透这个函数有哪些变量?有什么作用?

大家会怎么去设计node的函数和回调,便于阅读理解?

node.js async

瘋花雪月夜 10 years, 11 months ago

promise co 多的是

肝不存在的 answered 10 years, 11 months ago

promise can save your ass

几千D花ひら answered 10 years, 11 months ago

saloy answered 10 years, 11 months ago

可瞅下我的 https://github.com/laomayi/nodeDHT 代码组织方式. 还有就是善用emitter这个东西, 比如:


 var events = require("events")
var emitter = new EventEmitter()

emitter.on("addUser", function(username) {
    //do something
});

emitter.on("delUser", function(uid) {
    // do something
})

app.get("/user/add/:username", function(req, res) {
    emitter.emit("addUser", req.params.username);
})

app.get("/user/del/:uid", function(req, res) {
    emitter.emit("delUser", req.params.uid);
})

这样你的代码就的回调就不多了, 看起来就像是PHP代码的自定函数随时调用一样,

最关键的是, 能避免这个蛋疼的语言就尽量避免.

JKing answered 10 years, 11 months ago

关于异步和回调,
Promise才是救世主
async.js是邪教

自己的业务逻辑一律用Promise表示结果,三方的库第一时间promisify,异常通过promise reject处理

顺便推荐Promise库bluebird
coffeescript和它派生的 coco LiveScript

晒最近刚写的一个简单小站代码 https://github.com/mcfog/higari
express, bluebird, 前后端分离,redis缓存
业务是爬虫扒别的站自己组织展示

下面是吐列表的一个controller,扒列表,循环扒详情组装好json输出


 getList = require \../ojisan/list
getDetail = require \../ojisan/detail

app <- ->
  module.exports = it

req, res, next <- app.get /^\/rank\/season\/(\d{4}-\d{1,2}).json/

getList "/anime/browser/airtime/#{req.params.0}"
.map (entry)->
  getDetail entry.id
  .then ->
    entry.detail = it

    entry

.then -> res.json it
.timeout 30_000
.catch ->
  console.error it
  res.end it.toString!

幻灭D帝王 answered 10 years, 11 months ago

前段时间的项目使用了Nodejs,中间经过过一次重构,基于Express框架,分享一下经验:

  1. 流程Promise化
    基于Nodejs的回调语法,你会写出大量下面这种代码:

 doAsync1(function () {
      doAsync2(function () {
        doAsync3(function () {
          doAsync4(function () {})
        })
      })
    })

这就是所谓的 “回调黑洞” 了,采用回调写法最大的问题有两个:

一是异常问题的处理,假设你的 doAsync4 依赖 doAsync3 的返回值,而 doAsync3 函数又依赖于 doAsync2 的返回值,如果 doAsync2 函数没有返回预想的结果,那么回调仍会继续执行,而如果你的代码里没有做良好的错误处理,可能代码会一直执行到 doAsync4 ,debug的痛苦可想而知。

二是业务流程的变更,现在我们假设要更改业务流程, doAsync2 发生在 doAsync1 之前,你的重构工作量有多少?

这时我们就要考虑使用 Promise 了,我并不打算在这里详细的介绍Promise,有趣的是SF就有对于Promise分析良好的系列文章: 深入理解Promise五部曲
以及目前常见的回调写法替代方案 Node.js回调黑洞全解:Async、Promise 和 Generator

上述代码如果用Promise来重构结果会是这样的:


 doAsync1
      .then(function (data1) {
        return data1
      })
      .then(function (data2) {
        return doAsync2(data2)
      })
      .then(function (data3) {
        return doAsync3(data3)
      })
      .then(function () {
        res.send('数据处理成功')
      })
      .error(function (error) {
        res.send('Error: ' + error)
      })

  1. 应用分层
    在应用中分出 Controller 层和 Model 层,不要把页面渲染和逻辑都塞到 Router 层。理想的结果是: Controller + Model + Router + Filter ,Filter层即是过滤,也可以理解为中间件,可以写一些过滤规则,比如最常见的要求用户登录:

 module.exports = {
      '/homepage': {
        get: ['auth.requireLogin', 'auth.requireAdmin']
      }
    }

在项目的上一次重构中,我砍掉了Router层,增加了一层API,将页面渲染和流程都放在Controller,而数据处理都在API,这样方便未来做数据验证和单元测验:

Controller层


 module.exports = {
      '/customer': {
        get: function (req, res) {
          var userInfo = req.body.data

          User
            .create(userInfo)
            .success(function () {})
        },
        post: function () {}
      }
    }

API层


 module.exports = {
      '/customer': {
        get: function () {
          User
            .info()
            .success(function (requiredData) {
              res.send(requiredData)
            })
        }
      }
    }

Model层取决于你用的数据库,流程的Promise化客观上要求了你的Model也要Promise化,Node上常见数据库的ORM都提供了Promise的写法,对应去看就好了。Express并不提供这种Restful路径的写法,我参考Rabbit框架(感谢芋头)基于这个插件改了改: rainbow

最后来看看成果,重构前的代码如下:

图片描述

重构之后(数据都放在API层了):

图片描述

那么问题来了,写Nodejs哪种写法强?

啃西瓜的魂 answered 10 years, 11 months ago

Your Answer