0

所以在我的服务器代码中,变量邀请在成功函数之外是未定义的。

function getInvites(id){
    var InvitesTable = tables.getTable("Invites").where({"PlanID": id}).select("UserID","Attending");
    var invites;
    InvitesTable.read({ success: function(resultss) { 
                           invites = resultss;
                           console.log(invites); //works here
                           }});
    console.log(invites); //undefined here
}

从类似的问题中,我意识到它是因为它是异步的。所以成功函数调用是在console.log(invites); //undefined here调用之后运行的。

我的问题是如何在 Windows Azure 中阻止它?

添加代码

function read(query, user, request) {

        request.execute({
            success: function(results) {
                results.forEach(function(r) {

                    getInvites(r.id, function(invites) {
                        r.invites = invites;
                    });
                });
                request.respond();
            }
        });

}

function getInvites(id, cb){
    var InvitesTable = tables.getTable("Invites").where({"PlanID": id}).select("UserID","Attending");
    InvitesTable.read({ success: function(results) {
                           if (cb) cb(results);
                           }});
}
4

2 回答 2

5

您不会“停止”,而是围绕您使用的任何环境的异步特性来设计应用程序。

我假设你正在尝试做这样的事情:

function getInvites(id){
    var InvitesTable = tables.getTable("Invites").where({"PlanID": id}).select("UserID","Attending");
    var invites;
    InvitesTable.read({ success: function(resultss) { 
                           invites = resultss;
                           }});
    return invites;
}

// later...

var invites = getInvites(someId);
//do something with `invites`

这显然是行不通的,因为您invites在异步调用完成之前返回值。

相反,您以异步风格编写应用程序:

function getInvites(id, cb){
    var InvitesTable = tables.getTable("Invites").where({"PlanID": id}).select("UserID","Attending");
    InvitesTable.read({ success: function(resultss) { 
                           if (cb) cb(resultss);
                           }});
}

// later...

getInvites(someId, function(invites) {
    //do something with `invites`
});

为了简单起见,这省略了错误处理代码,因此您也必须添加它。


在查看完整代码后,您似乎遇到了管理许多并行异步操作的简单问题。考虑会发生什么:您的循环运行,迭代n 个对象的数组。对于每个,您调用getInvites,它开始一个数据库请求并返回。

这意味着您的循环运行得非常快,但是现在您有n 个未完成的数据库请求,您必须等待才能调用request.respond().

一个非常基本的解决方案是计算调用回调的次数getInvites,然后在该次数达到n时最终完成请求。

但是,每次发出异步请求时手动管理此簿记既费时又容易出错。这是流控制库非常有用的情况。我将Deferred在这个例子中使用 jQuery,因为它可能对你来说已经很熟悉了(即使你不知道你以前实际使用过它——如果你曾经使用过 jQuery 的 XHR API,那么你已经使用过Deferreds)。

鉴于您处于服务器环境中,您显然没有 jQuery;但是,有些人只提取了您所需的代码Deferred

一旦我们Deferred为每个待处理的请求设置了 s,我们就可以使用它来注册一个回调,该回调仅在所有待处理的 s 完成when后才被调用。Deferred

function read(query, user, request) {

        request.execute({
            success: function(results) {
                var dfds = [];
                for (var i = 0; i < results.length; i++) {
                    dfds.push(getInvites(results[i].id)); // Makes an array of Deferreds representing
                                                          // each of our pending requests.
                }


                Deferred.when.apply(Deferred, dfds) // see details below
                    .done(function() {
                        for (var i = 0; i < results.length; i++) {
                            results[i].invites = arguments[i]; // Copy each set of invites to each result
                        }
                        request.respond(); // We're done!
                    })
                    .fail(function() {
                        // Handle errors here
                    });
            }
        });

}

function getInvites(id){
    var dfd = new Deferred(); // Create a new Deferred, which automatically starts in the 'pending' state
    var InvitesTable = tables.getTable("Invites").where({"PlanID": id}).select("UserID","Attending");
    InvitesTable.read({ success: function(results) {
                           dfd.resolve(results); // When we get data back, we 'resolve' the Deferred --
                                                 // in other words, say its operation is done,
                                                 // and pass along the operation's results.
                           },
                        error: function(err) { // TODO: Not sure if this is how the API you're using handles errors
                            dfd.reject(err); // Marks the Deferred as failed.
                        }});

    return dfd.promise(); // We (synchronously) return the Promise.  The caller can attach event handlers
                          // to the Promise, which are invoked when we eventually resolve or reject the Deferred.
}

笔记:

  • jQuery.when(或在这种服务器端情况下,Deferred.when)通常希望您传递固定数量的Deferreds 作为参数:

    $.when(dfd1, dfd2).done(function(result1, result2) { ... });
    

    但是,我们有一个可变数量的Deferreds,所以我们必须一个sapply数组,然后在处理程序中,通过隐式对象访问每个结果。Deferredwhendonearguments

  • Array.forEach(...) 很慢。在大多数情况下,最好使用常规for循环。

于 2013-01-14T21:42:55.400 回答
0

我偶然发现了对同步数据库访问的同样需求,所以我编写了一个名为query-synchronizer 的小模块。想法是计算查询开始和结束的次数。如果所有开始计数等于结束计数,则将执行其他部分代码。您的代码如下所示:

var synchronizer = require('query-synchronizer');

function read(query, user, request) {
    request.execute({
        success: function(results) {
            results.forEach(function(r) {
                var InvitesTable = tables.getTable("Invites").where({"PlanID": r.id}).select("UserID","Attending");
                synchronizer.read(InvitesTable, function(results){
                    r.invites = invites;
                });

            });
            synchronizer.done(function(){
                request.respond();
            });
        }
    });

}
于 2013-10-28T16:35:39.683 回答