使链接的函数等待彼此执行

时间:2015-04-03 02:08:13

标签: node.js

如何使链接函数等到它之前的函数,才能正确执行?

我的摘录来自我的模块:

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 - 函数不会等待login完成(意思是,它在登录返回_this之前执行)。那么如何让它等待呢?

2 个答案:

答案 0 :(得分:2)

你不能使Javascript"等待"在执行链中的下一个调用之前。整个链将按顺序立即执行,无需等待任何异步操作完成。

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

jQuery为jQuery动画做了类似的事情(它为所有链式动画实现了一个队列,并在完成上一个动画时依次调用每个动画)。但是,它的实现比你在这里的实现更简单,因为它只适用于jQuery动画(或遵循正确约定的手动排队函数),而不适用于其他jQuery方法。您提议混合使用三种不同的方法,其中两种方法甚至不同步。

因此,为了缩短篇幅,如果你让所有方法都遵循一套惯例,那么你所要求的可能就是这样,但这并不容易。由于您似乎只有一个异步操作,我想知道您是否可以做这样的事情。你没有显示.exec()方法的作用,但如果只是在链的末尾调用一些其他函数,那么你在链中只有一个异步方法,所以你可以让它采取回调并执行此操作:

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

我正在努力做到这一点,但这不是我能在不到一个小时内编写和测试的东西,而且你必须提供一些想法,你想要对发生的错误做些什么链中的任何地方。非异步错误可能只会引发异常,但是在异步操作完成后发生的异步错误甚至非异步错误都不能抛出,因为没有好方法可以捕获它们。因此,错误处理变得复杂,你真的不应该走上一条没有预料到适当错误处理的设计路径。

我想的越多,我就越想要获得一个用于处理异步操作顺序的库(例如异步模块),并将其用于排队操作或者您想要给予在链接和使用承诺支持您的操作顺序。在我考虑如何使用异步操作在任务队列中进行错误处理时,我想到所有这些问题都已经在promises中处理(通过拒绝传播错误并在异步处理程序中捕获异常承诺拒绝等...)。使用异步操作进行错误处理很困难,这是构建异步操作排序的承诺的一个重要原因。


所以,现在考虑使用promises来解决这个问题,你可以做的是让每个异步方法返回一个promise(你可以通过一个带有许多promise库的调用来满足整个请求模块,比如Bluebird)。然后,一个.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
});

这不是链接,但它会让你在几分钟内完成,而不是可能需要花费很长时间才能编写的东西(具有强大的错误处理功能)。

答案 1 :(得分: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);
    });
});