CNode

使用Jscex改进Node Club(4):改写首页

Jjeffz发布于14 年前最后回复14 年前18 回复8999 浏览0 收藏

原文链接

tag_ctrl.get_all_tags(function (err, tags) { ... });
topic_ctrl.get_topics_by_query(..., function (err, topics) { ... });
topic_ctrl.get_topics_by_query(..., function (err, hot_topics) { ... });
user_ctrl.get_users_by_query(..., function (err, users) { ... });
user_ctrl.get_users_by_query(..., function (err, tops) { ... });
topic_ctrl.get_topics_by_query(..., function (err, no_reply_topics) { ... });
topic_ctrl.get_count_by_query(..., function (err, all_topics_count) { ... });
/********** Jscex ************/
var Jscex = require("../libs/jscex").Jscex;
var Jscexify = Jscex.Async.Jscexify;

exports.get_topics_by_query_async = Jscexify.fromStandard(get_topics_by_query);
exports.get_count_by_query_async = Jscexify.fromStandard(get_count_by_query);
function (request, response) {
    var tags = tag_ctrl.get_all_tags(); // 标签
    var topics = topic_ctrl.get_topics_by_query(...); // 最新话题
    var hot_topics = topic_ctrl.get_topics_by_query(...); // 热门话题
    var stars = user_ctrl.get_topics_by_query(...); // 明星用户
    var tops = user_ctrl.get_users_by_query(...); // 得分最高用户
    var no_reply_topics = topic_ctrl.get_topics_by_query(...); // 无回复话题
    var topic_count = topic_ctrl.get_count_by_query(...); // 话题总数

    response.render("index", { ... }); // 输出HTML
}
var indexAsync = eval(Jscex.compile("async", function (req, res) {
    var page = Number(req.query.page) || 1;
    var limit = config.list_topic_count;

    var data = {
        current_page: page,
        list_topic_count: limit
    };

    data.topics = $await(topic_ctrl.get_topics_by_query_async(...));

    data.hot_topics = $await(topic_ctrl.get_topics_by_query_async(...));

    data.stars = $await(user_ctrl.get_users_by_query_async(...));

    data.tops = $await(user_ctrl.get_users_by_query_async(...));

    data.no_reply_topics = $await(topic_ctrl.get_topics_by_query_async(...));

    var all_topics_count = $await(topic_ctrl.get_count_by_query_async(...));
    data.pages = Math.ceil(all_topics_count / limit);

    data.tags = $await(tag_ctrl.get_all_tags_async());

    // 计算最热标签
    data.hot_tags = _.chain(data.tags)
        .sortBy(function (t) { return -t.topic_count; })
        .first(5);

    // 计算最新标签
    data.recent_tags = _.chain(data.tags)
        .sortBy(function (t) { return -t.create_at.valueOf() })
        .first(5);

    res.render('index', data);
}));

exports.index = Unjscexify.toRequestHandler(indexAsync);
Jscex.Unjscexify = {
    toRequestHandler: function (fn) {
        return function (req, res, next) {
            fn(req, res).addEventListener("failure", function () {
                next(this.error);
            }).start();
        }
    }
}
tags.sort(function (tag_a, tag_b) {
    if (tag_a.topic_count == tag_b.topic_count) return 0;
    if (tag_a.topic_count > tag_b.topic_count) return -1;
    if (tag_a.topic_count < tag_b.topic_count) return 1;
});
tags.sort(function (tag_a, tag_b) {
    return tag_b.topic_count - tag_a.topic_count;
});
var indexAsync = eval(Jscex.compile("async", function (req, res) {
    var page = Number(req.query.page) || 1;
    var limit = config.list_topic_count;

    var data = $await(Task.whenAll({
        topics: topic_ctrl.get_topics_by_query_async({}, {
            skip: (page - 1) * limit,
            limit: limit,
            sort: [['last_reply_at', 'desc']]
        }),
        hot_topics: topic_ctrl.get_topics_by_query_async({}, {
            limit: 5,
            sort: [['visit_count', 'desc']]
        }),
        stars: user_ctrl.get_users_by_query_async(
            { is_star: true },
            { limit: 5 }
        ),
        tops: user_ctrl.get_users_by_query_async({}, {
            limit: 10,
            sort: [['score', 'desc']]
        }),
        no_reply_topics: topic_ctrl.get_topics_by_query_async(
            { reply_count: 0 },
            { limit: 5, sort: [['create_at', 'desc']] }
        ),
        tags: tag_ctrl.get_all_tags_async(),
        all_topics_count: topic_ctrl.get_count_by_query_async({})
    }));

    data.current_page = page;
    data.list_topic_count = limit;
    data.pages = Math.ceil(data.all_topics_count / limit);

    // 计算最热标签
    data.hot_tags = _.chain(data.tags)
        .sortBy(function (t) { return -t.topic_count; })
        .first(5);

    // 计算最新标签
    data.recent_tags = _.chain(data.tags)
        .sortBy(function (t) { return -t.create_at.valueOf() })
        .first(5);

    res.render('index', data);
}))
查看回复

回复 (18)

H
hpf1908#1·14 年前

先顶一个,建议出个bench mark 看看,数据才是最有说服力的哈

S
sumory#2·14 年前

async是可以选择性并行和串行的,eventproxy没用过,去看看

J
jeffz#4·14 年前

那个衣服拿来!

K
kongwu#5·14 年前
引用 jeffz那个衣服拿来!

@jeffz 没有仔细看,对于评论也是有要求的。不过拿衣服对于你来说简直是手到擒来的事情。

W
wenhua#6·14 年前

想知道,Jscex 能和coffeescript 结合一起用吗?

N
newcoder#7·14 年前

nodeclub里随处可见的proxy.trigger()的确让代码不够优雅,另外一个就是楼主提出的错误处理问题,分散不易处理,EventProxy没有提供相应的处理机制。下面的代码,人家还等你去trigger呢,你直接就return了,太粗鲁了点。

	if(err) return next(err);
	proxy.trigger('topics',topics);

其实,async.js已经比较好的解决了这个问题。这里我不喜欢同步化,不伦不类,异步问题就用异步的解决方案,访问数据库时并行执行是有好处的,数据库不一定在同一机器上。async.js使用也简单,我花几分钟就把主页改过来了,跑了一下没问题,下面用async.js的代码,看起来顺多了,是个人喜好吗?另外,nodeclub里对mongodb的使用,不如用mysql了。好东西还看怎么用啊。

var async = require('../async');

exports.index = function(req,res,next){
    var page = Number(req.query.page) || 1;
    var limit = config.list_topic_count;

    var render = function(tags,topics,...){
            ...
        res.render('index',{...});
    };  

    async.parallel({
        tags: function(callback) {
            tag_ctrl.get_all_tags(callback);
        },      
        hot_topics: function(callback) {
            opt = {limit:5, sort:[['visit_count','desc']]};
            topic_ctrl.get_topics_by_query({},opt,callback);
        },
            ...
    },

    function(err, results){
        if (err) return next(err);
        render(results.tags,...);
    });

};
K
kamaliang#8·14 年前

这一系列的文章都非常不错, 新手受益匪浅啊! 继续关注ing..

J
jeffz#9·14 年前
J
jeffz#10·14 年前
引用 newcodernodeclub里随处可见的proxy.trigger()的确让代码不够优雅,另外一个就是楼主提出的错误处理问题,分散不易处理,EventProxy没有提供相应的处理机制。下面的代码,人家还等...

显然是个人喜好,同步化有什么不伦不类的,好多语言早这么做了,用同步的方式表达异步逻辑就是异步问题的解决方法之一,非要用回调什么解决异步问题这个叫做禁锢思维。编程的目的在于易于表达逻辑,清晰表达,易于抽象等等,所以产生各种编程范式,没有说一定要怎样解决的。

还有你用async处理这里当然OK,如果稍微有点逻辑,例如循环啊,判断啊啥啥的。我不是提到Leaky Abstraction了吗,普通的异步抽象方式都比较Leaky,Jscex是遵循JS的语义和表达方式,灵活性是图灵完备的,所以比普通抽象要少Leaky得多。

还有我说了嘛,如果你能确定访问资源可以并行,那就并行,现在这种么就是盲目并行,我保证数据库跑在一台机器上且不能动态伸缩。还有其实MongoDB很好的啊,很方便,MySQL还要键表,还不能保存复杂结构,很麻烦。

N
newcoder#11·14 年前
引用 newcodernodeclub里随处可见的proxy.trigger()的确让代码不够优雅,另外一个就是楼主提出的错误处理问题,分散不易处理,EventProxy没有提供相应的处理机制。下面的代码,人家还等...
  1. 复杂逻辑在async里一点问题都没有。nodeclub首页这个例子恰巧很简单。
  2. coding的时候很难保证以后数据库怎么部署的,再说也没必要。
  3. 纯属个人偏好,我的偏好比较大众化的。 关于mongodb, 我没表达清楚,我其实想说:nodeclub里对mongodb的使用方式,不如用mysql了,目前的这种使用方式,就是折腾自己,再说一句"不伦不类"吧。还是那句话:好东西还看怎么用啊。
N
newcoder#12·14 年前
引用 newcodernodeclub里随处可见的proxy.trigger()的确让代码不够优雅,另外一个就是楼主提出的错误处理问题,分散不易处理,EventProxy没有提供相应的处理机制。下面的代码,人家还等...

@sumory 有点误会,我不是建议放弃mongodb。不过按目前这种存储结构设计,用mysql会好很多。目前的schema是为关系数据库设计的。

N
newcoder#15·14 年前

在nodejs里面搞同步化,我有一点提醒:nodejs中执行的函数必须是迅速返回的,包括回调函数也是如此。这是nodejs之所以称为异步框架的原因,也是nodejs对应用代码的要求。不遵循这个要求的应用是不可能在真实环境中跑起来的,当然做做research或者demo是可以的。

J
jeffz#16·14 年前
引用 newcoder在nodejs里面搞同步化,我有一点提醒:nodejs中执行的函数必须是迅速返回的,包括回调函数也是如此。这是nodejs之所以称为异步框架的原因,也是nodejs对应用代码的要求。不遵循这个...

Jscex的神奇之处就是用同步的形式的代码,但是每一步$await都是异步操作,不是阻塞的。了解一下Jscex吧,不要被传统异步编程模式限制了思维。

J
jeffz#17·14 年前
引用 newcodernodeclub里随处可见的proxy.trigger()的确让代码不够优雅,另外一个就是楼主提出的错误处理问题,分散不易处理,EventProxy没有提供相应的处理机制。下面的代码,人家还等...

@newcoder 多给点例子吧,我是不清楚怎么方便地使用async写while啊if什么的。Coding的时候当然要关注数据库怎么部署的,理想年代还未到来。MongoDB多方便啊,字段都不用定义的,想加字段随意。

参与回复
登录后即可参与回复。登录