2015-04-03 19 views
0

如何让链式函数在它之前等待函数执行正确?让链式函数等待对方执行

我从模块此摘录:

var ParentFunction = function(){ 
    this.userAgent = "SomeAgent"; 
    return this; 
} 

ParentFunction.prototype.login = function(){ 
    var _this = this; 

    request.post(
     url, { 
     "headers": { 
      "User-Agent": _this.userAgent 
     } 
    }, function(err, response, body){ 
     return _this; 
    }) 
} 

ParentFunction.prototype.user = function(username){ 
    this.username = username; 
    return this; 
} 

ParentFunction.prototype.exec = function(callback){ 
    request.post(
     anotherURL, { 
     "headers": { 
      "User-Agent": _this.userAgent 
     } 
    }, function(err, response, body){ 
     callback(body); 
    }) 
} 

module.exports = parentFunction; 

这是从我的服务器中:

var pF = require("./myModule.js"), 
    parentFunction = new pF(); 

parentFunction.login().user("Mobilpadde").exec(function(data){ 
    res.json(data); 
}); 

的问题是,该user -function不会等待login到完成(意思是,它在登录之前执行返回_this)。那么我该如何让它等待呢?

回答

1

在执行链中的下一个呼叫之前,您无法使Javascript“等待”。整个链将按顺序立即执行,无需等待任何异步操作完成。

我能想到使这个结构工作的唯一方法是创建一个等待执行的事件队列,然后以某种方式监视该队列中异步的事情,以便知道何时执行队列中的下一个事件。这就要求每种方法都遵循某种标准约定,以便了解异步操作完成时该方法是否异步以及异步操作是否异步,以及链中是否存在错误时该如何操作。

jQuery为jQuery动画做了这样的事情(它为所有链接的动画实现一个队列,并在前一个动画完成时依次调用每个动画)。但是,它的实现比你在这里更简单,因为它仅适用于jQuery动画(或遵循适当约定的手动排队函数),而不适用于其他jQuery方法。你提出了三种不同的方法,其中两种甚至不是异步的。

因此,如果你想让所有的方法遵循一系列的约定,那么你所要求的可能会做得更多,但这并不容易。由于您似乎只有一个异步操作,所以我想知道您是否可以这样做。您不会显示.exec()方法的功能,但如果它只是在链的末尾调用某个其他函数,那么您在链中只有一个异步方法,因此您可以让它进行回调并做到这一点:

parentFunction.user("Mobilepadde").login(function(data) { 
    res.json(data); 
}); 

我正在这样做的排队手段,但它不是我可以编写和测试,在不到一个小时,你就必须提供什么样的一些想法你想要处理链中任何地方发生的错误。非异步错误可能会引发异常,但异步操作完成后发生的异步错误甚至非异步错误不能只是抛出,因为没有好的方法来捕获它们。所以,错误处理变得很复杂,你真的不应该着手一个不能预期适当的错误处理的设计路径。

我越想到这个,我想你或者想要得到一个旨在处理异步操作(如异步模块)的排序的库,并将其用于排队操作,或者您只想要放弃链接并使用承诺来支持操作的排序。当我考虑如何在异步操作的任务队列中进行错误处理时,我发现所有这些问题都已经在promise中处理过(通过拒绝传播错误并捕获异步处理程序中的异常承诺拒绝等)。对异步操作进行错误处理是很困难的,也是构建排序异步操作承诺的一个重要原因。


所以,现在想解决这个使用的承诺,你可以做的就是使每一个异步方法返回一个承诺(你可以用一个调用了许多承诺库,如蓝鸟promisfy整个请求模块)。然后,一个.login().exec()回报的承诺,你可以这样做:

var Promise = require('bluebird'); 
var request = Promise.promisifyAll(require('request')); 

ParentFunction.prototype.login = function(){ 
    return request.postAsync(
     url, { 
     "headers": { 
      "User-Agent": this.userAgent 
     } 
    }); 
} 

ParentFunction.prototype.exec = function(){ 
    return request.postAsync(
     anotherURL, { 
     "headers": { 
      "User-Agent": this.userAgent 
     } 
    }).spread(function(response, body) { 
     return body; 
    }) 
} 

parentFunction.login().then(function() { 
    parentFunction.user("Mobilpadde"); 
    return parentFunction.exec().then(function(data) { 
     res.json(data); 
    }); 
}).catch(function(err) { 
    // handle errors here 
}); 

这不是链接,但它可以让你在几分钟内去,而不是东西,可能需要很长一段时间写的(有可靠的错误处理) 。

+0

哦,dangit!我真的希望能够避免将对方的功能堆叠在一起。无论如何,我只是用缺少的exec函数更新了我的问题(并不是说它以任何方式改变了我的问题,但仍然如此)。 – Mobilpadde 2015-04-03 03:02:05

+0

@Mobilpadde - 查看我添加到我的答案的评论。我开始设计一个队列的路径,这个队列可以嵌入到你的对象中以进行链接工作,但是混合同步和异步操作的组合,然后在异步操作完成之后尝试找出适当的错误处理策略,问题变成一个相当大的问题。我建议采用不同的方法。如果所有操作都是同步的,则链接很容易。异步操作的混合使事情变得复杂。 – jfriend00 2015-04-03 03:15:51

+0

@Mobilpadde - 我添加了一个承诺风格的实现。 – jfriend00 2015-04-03 03:26:13

0

试试这个,看看它是否工作:

ParentFunction.prototype.login = function(callback){ 
    var _this = this; 
     request.post(
      url, { 
      "headers": { 
       "User-Agent": _this.userAgent 
      } 
     }, function(err, response, body){ 
      return callback(_this); 
     }) 
    } 
} 

在服务器端:

parentFunction.login(function(loggedin){ 
    loggedin.user("Mobilpadde").exec(function(data){ 
     res.json(data); 
    }); 
}); 
+0

是的,我知道这是可以做到的,但我真的宁愿不这样做,因为我试图了解链接。对不起,我已经在我的问题中说过了。 – Mobilpadde 2015-04-03 02:57:35