首页 > 技术文章 > 用订阅/发布者模式解决异步函数结果依赖的问题

yonglin 2017-11-20 20:16 原文

  我们都知道node是基于事件无阻塞i/o模型的,所以说大部分函数都是以异步实现的,请看下面代码:

 

db.query(sql1, function (err, data) {
    //code
})

db.query(sql2, function (err, data) {
    //code
})

 

  如果我们上述两个操作,结果之间没有什么联系,那很好,基于node的I/O无阻塞模型,每个操作都做着自己的事情,美滋滋~

  但是在一些情况下这两个操作的结果有联系的,比如说第一个操作从数据库中取出一个人的姓,第二个操作从数据库中取出同一个人的名。

  假设我们有一个需求就是我们要把这两个操作的结果组合起来变成一个人的姓名(这个操作貌似有点浮夸,嘻嘻),也就是说我们要组合成姓名这个操作必须是在姓和名都取到的情况下才能进行,可达鸭眉头一皱,发现事情并不简单。我们知道上述两个操作是异步操作,他们何时结束我们根本不能知道,只知道他结束时会执行回调函数。下面我们就用订阅发布者模式来解决它。请看下面代码:

 

var count = 0;
var results = {};
var done = function (key, value) {  //订阅,姓和名进行订阅
    results[key] = value;
    count++;

    if(count === 2) {
        //发布,也就是执行刚刚说的组成姓名的操作
var name = results.lastName + results.firtName;
} } db.query(sql1, function (err, data) { //这里data我们假设是从数据库取到姓 done("lastName", data); }) db.query(sql2, function (err, data) { //这里data我们假设是从数据库取到名 done("firstName", data); })

 

这样一来,每个i/o完成都会执行done方法,把取到的数据存到results对象中,也就是订阅,而当订阅数等于2的时候,也就是两个操作都完成并且都把数据存在results中了,那么就可以发布,也就是执行组成姓名的操作。利用订阅/发布者模式实现多对一,基本完成了我们的需求,但是这样的代码是丑陋的,我们用闭包和偏函数把代码优化并且抽象一下,以适合更多的需求。改进代码如下:

 

var after = function (times, callback) {
    var count = 0,
        results = {};
    return function (key, value) {
        results[key] = value;
        count++;
        if(count === times) {
            callback();
        }
    };
};

//对闭包的引用
var done = after(times, render);

 

ps:利用node的events模块我们还可以实现订阅/发布者的多对多模式。

参考资料:深入浅出Node.js第4章

 

推荐阅读