问题
So in my server code, variable invites is undefined outside of the success function.
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
}
From similar questions, I realize its because of it being asynchronous. So the success function call is run after the console.log(invites); //undefined here
call.
My question is how do I stop that in Windows Azure?
Added code
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);
}});
}
回答1:
You don't "stop that," you design your application around the async nature of whatever environment you're using.
I assume you're trying to do something like this:
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`
This obviously won't work, since you return the value of invites
before the async call completes.
Instead, you write your app in async style:
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`
});
This leaves out error handling code for the sake of simplicity, so you'd have to add that as well.
After seeing your full code, it looks like you have a simple problem of managing many parallel asynchronous operations. Consider what happens: your loop runs, iterating over an array of n objects. For each, you call getInvites
, which begins a database request and returns.
This means your loop runs very quickly, but now you have n outstanding database requests that you must wait on before you can call request.respond()
.
An extremely basic solution would be to do something like count the number of times your getInvites
callback is called, and then finally complete the request when that number reaches n.
However, it is time-consuming and mistake-prone to manage this bookkeeping manually every time you make async requests. This is a situation where flow control libraries are extremely useful. I will use jQuery's Deferred in this example, since it may already be familiar to you (even if you don't know you've actually used it before — if you've ever used jQuery's XHR API, you've used Deferred
s).
Given that you're in a server environment, you obviously don't have jQuery; however, there are people who have extracted only the code necessary for Deferred for you.
Once we have Deferred
s for every pending request, we can use when to register a callback that gets called only after all pending Deferred
s complete.
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.
}
Notes:
jQuery.when
(or in this server-side case,Deferred.when
) normally expects you to pass a fixed number ofDeferred
s as arguments:$.when(dfd1, dfd2).done(function(result1, result2) { ... });
However, we have a variable number of
Deferred
s, so we must apply an array ofDeferred
s towhen
and then in thedone
handler, access each result via the implicit arguments object.Array.forEach(...)
is slow. In most cases, it is better to use a regularfor
loop.
回答2:
I've stumbled on same need for synchronous DB access, so I wrote small module called query-synchronizer. Idea was to count how many times query was started and ended. If all started count was equal to ended count, other part of code would be executed. Your code would look like this:
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();
});
}
});
}
来源:https://stackoverflow.com/questions/14326849/synchronous-request-in-windows-azure