Dojo - variable assignment occurs too late in xhrPut - javascript

I have the following:
if(typeof searchDOM === "undefined"){
dojo.xhrPut({
url: addrPath + "/ContServlet?mod=1&act=23",
handleAs: "xml",
timeout: xhrTimeout(TIMEOUT_LRG),
load: function(dom, ioArgs){
if(dom instanceof Error){
console.error(dom);
} else{
cacheDOM = dom;
}
},
error: function(response, ioArgs){
xhrError(ioArgs, methodName);
}
});
}
The variable cacheDOM is a global variable declared(but not initialised) elsewhere in another script. It is an xml document containing the entire dom, and it is passed into:
the problem is, cacheDOM is undefined when it gets to fetchXml, and this is causing problems for methods like selectNode further down the function.
I haven't had much exposure to xhr calls, or things such as deferreds or promises, but I think that they may be able to help with this. How do i code this so that the rest of the method that this block is in will only execute if cacheDOM has been assigned the value of dom? Or if deferreds are the answer, how would i incorporate them into this code? The version of dojo i am using is 1.7.8

Well, the problem is indeed that you're using an XHR request which is asynchronous. So, the fetchXml function has to wait until that request is completed.
There are several ways to do this, you could call the fetchXml function from within the load function of dojo.xhrPut, but this is not really a good solution when your project grows because it creates a lot of dependencies on each other.
So, some smart people created an API for resolving asynchronous requests, called promises/deferreds.
So, what you have to do is assigning a new deferred to cacheDOM, for example:
require(["dojo/_base/Deferred"], function(Deferred) {
cacheDOM = new Defered();
});
Then, in the fetchXml() code you have to change your code a bit to do this:
function fetchXml() {
cacheDOM.then(function(realCache) {
console.log(realCache);
});
}
So in stead of directly using cacheDOM you have to wait for it using cacheDOM.then(). It will fire a callback when it's resolved, and the data will be available in realCache.
An alternative would be to call the entire fetchXml function when the XHR request has fired:
cacheDOM.then(fetchXml);
function fetchXml(cacheDOM) {
// Work with cacheDOM
}
This might take less work and less alteration to the fetchXml function depending on how much it relies on cacheDOM.
Then finally, inside your dojo.xhrPut you will have to do the following:
cacheDOM.resolve("My data");
Where "My data" would be the actual data which you would put inside cacheDOM.
DEMO: http://jsfiddle.net/rf20s9hb/1/

Related

Mixing sync and async javascript/jquery and getting a success function at the end

Wondering what the best solution to this problem is, also this is not my actual code structure or names but the simplest way to illustrate the problem.
I have a function which was purely used to perform an ajax call and load a template with jquery.
function load(template) {
$('#container').load(template, data, function() {
// complete code here
});
}
Focusing on the 3rd param in $.load(), namely a callback function that runs when the request is complete.
Now I have my load() function in another wrapper function:
function processTask(variable) {
load(variable);
}
The problem I have is I need some code to run after the ajax load is complete, however as my app has grown my wrapper function processTask may or may not invoke an ajax load so I can't perform my must needed code inside the complete callback.
Do I change my $.load() to perform synchronous or just manage my code better so that if I am calling a $.load() it puts my needed code in the callback and if not it places it where I need it to be?
I have read about javascript Promises and I'm unsure if they will help in this situation.
EDIT
So my processTask is an object method.
function classObj(name, fn) {
this.name = name;
this.processTask = fn;
this.load = function(template) {
$('#container').load(template, data, function() {
// complete code here
});
}
}
And in context I do this:
var task = new classObj('taskName', function() {
this.load('myFile.php');
// Or another function and not load() based on whats needed in the task.
});
Basically I have an object that I can add custom methods to at will and they can easily be called dynamically, until now they have always loaded a file.
First, change your load function to return the xhr from get (or ajax):
function load(template) {
return $.get('myFile.php', data, function(result) {
$('#container').html(result);
});
}
Then, within your code you can use when then to perform your code after the load completes if applicable:
var xhr;
/* ... */
if(something){
xhr = load(template);
}
/* ... */
if(xhr){
$.when(xhr).then(doSomething);
} else {
doSomething();
}
And in fact, this can be simplified using the fact that a non-deferred object passed to when (including undefined apparently) will execute the then immediately and get rid of the if:
$.when(xhr).then(doSomething);
If xhr is undefined then when will resolve immediately causing then to execute immediately.

Understanding jQuery.Deferred in the context of jQuey.AJAX (again)

I concede that, despite hours of reading and attempting, I am fundamentally unable to grasp something about Deferred promises and asynchrony in general.
The goal on my end is real, real simple: send some data to the server, and react to the contents of the response conditionally.
The response will always be a JSON object with save and error keys:
{ "save": true, "error":false}
// or
{ "save" : false,
"error" : "The server has run off again; authorities have been notifed."}
I have tried dozens and dozens of variations from the jQuery API, from other stackexchange answers, from tutorials, etc.. The examples all seem concerned with local asynchronous activity. When I need is some ability to be made aware when the AJAX request has either finished and returned a response I can inspect and make decisions about, or else to know that it's failed. Below, I've used comments to explain what I think is happening so someone can show me where I'm failing.
I know this is a repost; I am, apprently, worse than on average at grasping this.
var postData = {"id":7, "answer":"Ever since I went to Disneyland..."};
/* when(), as I understand it, should fire an event to be
responded to by then() when it's contents have run their course */
var result = $.when(
/* here I believe I'm supposed to assert what must complete
before the when() event has fired and before any chained
functions are subsequently called */
/* this should return a jqXHR object to then(), which is,
I'd thought, a queue of functions to call, in order,
UPON COMPLETION of the asynchronous bit */
$.post("my/restful/url", postData))
.then( function() {
/* since "this" is the jqXHR object generated in the $.post()
call above, and since it's supposed to be completed by now,
it's data key should be populated by the server's response—right? */
return this.data;
});
// alas, it isn't
console.log(result.data);
// >> undefined
Most examples I can find discuss a timeout function; but this seems, as I understand, to be a failsafe put in place to arbitrarily decide when the asynchronous part is said to have failed, rather than a means of stalling for time so the request can complete. Indeed, if all we can do is just wait it out, how's that any different from a synchronous request?
I'll even take links to a new read-mes, tutorials, etc. if they cover the material in a different way, use something other than modified examples from the jQuery API, or otherwise help this drooling idiot through the asynchronous mirk; here's where I've been reading to date:
jQuery API: Deferred
JQuery Fundamentals
jQuery Deferreds promises asynchronous bliss (blog)
StackOverflow: timeout for function (jQuery)
Update
This is in response to #Kevin B below:
I tried this:
var moduleA = {
var moduleB = {
postData: {"id":7, "answer":"Ever since I went to Disneyland..."};
save: function() {
return $.post("path/to/service", postData, null, "JSON");
}
};
var result = this.moduleB.save();
result.done(function(resp) {
if (resp.saved == true) {
// never reached before completion
console.log("yahoo");
} else {
console.log("Error: " + resp.error);
// >> undefined
}
});
}
You are over-complicating your code. You cannot get the data to outside of the callback, no matter how many deferred/promises you create/use (your sample creates 3 different deferred objects!)
Use the done callback.
var postData = {"id":7, "answer":"Ever since I went to Disneyland..."};
$.post("my/restful/url", postData).done(function (result) {
console.log(result.save, result.error);
});
You seem to have a misunderstanding of both asynchronous requests, the Promise pattern, and Javascripts mechanism of passing functions as an argument.
To understand what's really happening in your code I suggest you use a debugger and set some breakpoints in the code. Or, alternatively, add some console.logs in your code. This way you can see the flow of the program and might understand it better. Also be sure to log the arguments of the function you pass as an argument in the then()-method, so you understand what is passed.
ok you got it half right. the problem is that when you execute the console.log the promised is not yet fulfilled the asynchronous nature of the promises allows the code to execute before that ajax operation is done. also result is a deferred not a value, you need to handle your promised with .done instead of .then if you wish to return a value otherwise you'll continue passing promises.
so that said
var result={};
$.when(
$.post("my/restful/url", postData))
.done( function(data) {
result.data=data;
});
// here result is an object and data is a undefined since the promised has no yet been resolve.
console.log(result.data);

how do i callback with js function [duplicate]

This question already has answers here:
How do I return the response from an asynchronous call?
(41 answers)
Closed 9 years ago.
I'm having trouble with callbacks mainly because I don't understand how they're working (or supposed to work).
I have my function:
function checkDuplicateIndex(values, callback) {
$.ajax({
type: "POST",
url: url,
data: "command=checkIndexAlbumTracks&" + values,
dataType: "html",
success: function(data){
var returnValue = data.d;
callback(returnValue);
}
});
}
And then within a submit event, how do I properly call checkDuplicateIndex so that I can alert() the value?
This ended up being a long answer, so I'm going to try to split it into pieces.
Functions in Javascript
So within javascript, a function is an object that can be passed around, assigned to a variable, etc, just like any other data type. The difference is that a function, rather than being a string of text, number, etc, is a block of code waiting to be executed.
This is often confusing to people starting out with programming because usually when you write code, it is executed when you run the program. But for functions, this is not the case. When you write code inside a function, it waits there not executing until you call the function. If you do not call the function, the code is never executed. Let's check out a quick example:
function say_hello(){
console.log('hello!');
}
What you see here is called a function declaration. This means you are creating a function, which is a block of code waiting to be executed. If you run this code, nothing will be logged to the console. Now let's look at a function call.
function say_hello(){
console.log('hello!');
}
say_hello();
So here we declare the function just like before, but below we call it. A function call is just the name of the function followed by open and close parens. If the function takes arguments, they will be inside the parens, but no need to worry about that for now. If you were to run this code, you would in fact see hello! logged to the console, because the function was called, which executes the code inside.
Asynchronous Code
Now, let's switch gears for a second. When you make a jquery ajax call, jquery abstracts a lot of code into the library. They take care of setting up the XMLHttpRequest, firing it off to the place you specify, and collecting the result, and they do this in a way that works cross-browser. But since javascript is asynchronous, as soon as the ajax call goes off, javascript keeps executing code after the ajax call, because who wants to wait around for someone else's server to respond while you could be still getting in that work. So if you fire off something like this:
$.ajax({
url: 'http://google.com',
success: function(){ console.log('done!') }
});
console.log('after ajax call');
...you may be surprised to find that it logs after ajax call before logging done!. This is because, as stated earlier, in javascript calls that deal with I/O are often asynchronous.
So if the ajax call is made and it immediately continues executing code even if the ajax call has not finished, how can we specify code that will run when it's finished? This is where everything comes together. By providing jquery with a function, which as we remember is a block of unexecuted code, we can provide a way for ourselves to write code that is executed only after the ajax call has finished by passing the block of unexecuted code to jquery and saying essetially "hey jquery, take this code, and when the ajax call is finished, call it and pass in any data you got out of it." How convenient!
The way we do this is through the success and error properites of jquery's ajax function. If the request was successful, it will call the function we pass to success, and I assume you can guess what happens if there was an error.
Putting It All Together
Asynchronous code and first class functions are two of the most confusing parts about javascript, and once you understand these two concepts, you'll be in a great spot, although it may take a while to get there. So it's important to think carefully about it and experiment. Let's talk through a couple ways to handle the example you are working with here, about jquery ajax.
First, we can try making our own function and passing the name of the function to the success handler. Then when it comes back, it will call the function. Let's take a look:
var my_callback = function(data){
console.log(data);
}
$.ajax({
url: 'http://google.com',
success: my_callback
});
This is an interesting way of doing it. Here we have assigned an anonymous function to a variable, then passed the variable name to the success handler. This will work fine. Now let's try another way:
function my_callback(data){
console.log(data);
}
$.ajax({
url: 'http://google.com',
success: my_callback
});
Here, we define a named function and do the same thing. This will work the same way. Named functions in javascript can actually be declared after the are used, so you could move the function declaration below the ajax call and it would still work. Try this out. This is a nice advantage to named functions.
Finally, let's take a look at a third way we could handle it:
$.ajax({
url: 'http://google.com',
success: function(data){
console.log(data);
}
});
Here, we define an anonymous function right inline on the success handler. This works exactly the same as either of the other two options. In all three of these ways, jquery receives a function declaration, and calls it when it needs to, which is after the ajax request has come back.
I know this is a super long answer, but what you are confused about here are some of the core concepts of javascript, and I thought it would be more helpful to go over them here than to just solve your problem and give you the answer without explanation of the concepts. In fact, I haven't actually tackled your problem here at all, but you will easily be able to solve it yourself after understanding these concepts. If you are still having trouble, drop a comment and I'll try to clarify more.
Given the above code, you would call it like this within your submit handler:
var values = '…';
checkDuplicateIndex(values, function(returnValue) {
alert(returnValue);
// additional processing here...
});

Wait for asynchronous function to finish without adding callback

I'm writing tests for my Node.js/Express/Mongoose project using Mocha and Should.js, and I'm testing out my functions that access my MongoDB. I'm want these tests to be completely independent from the actual records in my database, so I want to create an entry and then load it, and do all my tests on it, then delete it. I have my actual functions written (I'm writing tests after the entire project is complete) such that the create function does not have a callback; it simply just renders a page when it's done. In my tests script, I call my load_entry function after I call create, but sometimes create takes longer than usual and thus load_entry throws an error when it cannot actually load the article since it has yet to be created. Is there any way to make sure an asynchronous function is finished without using callbacks?
Please let me know if there is any more info I can provide. I looked all over Google and couldn't find anything that really answered my question, since most solutions just say "use a callback!"
Use what is known as a promise
You can read more about it here.
There are lots of great libraries that can do this for you.
Q.js is one I personally like and it's widely used nowadays. Promises also exist in jQuery among many others.
Here's an example of using a q promise with an asynchronous json-p call: DEMO
var time;
$.ajax({
dataType: 'jsonp',
type: 'GET',
url: "http://www.timeapi.org/utc/now.json",
success: function (data) {
time = data;
},
error: function (data) {
console.log("failed");
}
})
.then(function(){ // use a promise library to make sure we synchronize off the jsonp
console.log(time);
});
This is definitely the kind of thing you want a callback for. Barring that, you're going to have to write some kind of callback wrapper that polls the database to determine when it has finished creating the relevant records, and then emits an event or does some other async thing to allow the test to continue.
Since the only native way to do asynchronous things are: setTimeout, setInterval and addEventListener, and they all take a callback you will eventually have to use a callback somewhere.
However, you can hide that by using Promises/A, also known as Deferreds.
Your code could look like this:
db.create_entry("foo", data).done(function (entry) {
db.delete_entry(entry).done(function () {
console.log("entry is deleted");
});
});
Using then-chaining:
db.create_entry("foo", data).then(function (entry) {
return db.delete_entry(entry);
}).done(function () {
console.log("entry is deleted");
});;
I found a solution that works. What I did was to add a callback to my function (next) and only call it if it's specified (i.e., for the tests):
//more stuff above
article.save(function(err){
if (!err) {
console.log(req.user.username + ' wrote ' + article.slug)
return next() || res.redirect('/admin')
}
return next(err) || res.render('articles/entry_form', {
title: 'New Entry',
article: article,
})
})
This way, when I run the actual server and no callback is specified, it won't throw an error, because it will simply return the res.render statement.

Variables set during $.getJSON function only accessible within function

This may be more of a scoping question. I'm trying to set a JSON object within a $.getJSON function, but I need to be able to use that object outside of the callback.
var jsonIssues = {}; // declare json variable
$.getJSON("url", function(data) {
jsonIssues = data.Issues;
});
// jsonIssues not accessible here
A similar question like this one was asked in another post, and the consensus was that anything I need to do with the JSON objects needs to be done within the callback function, and cannot be accessed anywhere else. Is there really no way that I can continue to access/manipulate that JSON object outside of the $.getJSON callback? What about returning the variable, or setting a global?
I'd appreciate any help. This just doesn't seem right...
UPDATE:
Tried setting the $.ajax() async setting to false, and running through the same code, with no luck. Code I tried is below:
var jsonIssues = {}; // declare json variable
$.ajax({ async: false });
$.getJSON("url", function(data) {
jsonIssues = data.Issues;
});
// jsonIssues still not accessible here
Also, I've had a couple responses that a global variable should work fine. I should clarify that all of this code is within $(document).ready(function() {. To set a global variable, should I just declare it before the document.ready? As such:
var jsonIssues = {};
$(document).ready(function() {
var jsonIssues = {}; // declare json variable
$.getJSON("url", function(data) {
jsonIssues = data.Issues;
});
// now accessible?
}
I was under the impression that that a variable declared within document.ready should be "globally" accessible and modifiable within any part of document.ready, including subfunctions like the $.getJSON callback function. I may need to read up on javascript variable scoping, but there doesn't seem to be an easy to achieve what I'm going for. Thanks for all the responses.
UPDATE #2:
Per comments given to answers below, I did use $.ajax instead of .getJSON, and achieved the results I wanted. Code is below:
var jsonIssues = {};
$.ajax({
url: "url",
async: false,
dataType: 'json',
success: function(data) {
jsonIssues = data.Issues;
}
});
// jsonIssues accessible here -- good!!
Couple follow-up comments to my answers (and I appreciate them all). My purpose in doing this is to load a JSON object initially with a list of Issues that the user can then remove from, and save off. But this is done via subsequent interactions on the page, and I cannot foresee what the user will want to do with the JSON object within the callback. Hence the need to make it accessible once the callback complete. Does anyone see a flaw in my logic here? Seriously, because there may be something I'm not seeing...
Also, I was reading through the .ajax() jQuery documentation, and it says that setting async to false "Loads data synchronously. Blocks the browser while the requests is active. It is better to block user interaction by other means when synchronization is necessary."
Does anyone have an idea how I should be blocking user interaction while this is going on? Why is it such a concern? Thanks again for all the responses.
$.getJSON is asynchronous. That is, the code after the call is executed while $.getJSON fetches and parses the data and calls your callback.
So, given this:
a();
$.getJSON("url", function() {
b();
});
c();
The order of the calls of a, b, and c may be either a b c (what you want, in this case) or a c b (more likely to actually happen).
The solution?
Synchronous XHR requests
Make the request synchronous instead of asynchronous:
a();
$.ajax({
async: false,
url: "url",
success: function() {
b();
}
});
c();
Restructure code
Move the call to c after the call to b:
a();
$.getJSON("url", function() {
b();
c();
});
Remember that when you supply a callback function, the point of that is to defer the execution of that callback until later and immediately continue execution of whatever is next. This is necessary because of the single-threaded execution model of JavaScript in the browser. Forcing synchronous execution is possible, but it hangs the browser for the entire duration of the operation. In the case of something like $.getJSON, that is a prohibitively long time for the browser to stop responding.
In other words, you're trying to find a way to use this procedural paradigm:
var foo = {};
$.getJSON("url", function(data) {
foo = data.property;
});
// Use foo here.
When you need to refactor your code so that it flows more like this:
$.getJSON("url", function(data) {
// Do something with data.property here.
});
"Do something" could be a call to another function if you want to keep the callback function simple. The important part is that you're waiting until $.getJSON finishes before executing the code.
You could even use custom events so that the code you had placed after $.getJSON subscribes to an IssuesReceived event and you raise that event in the $.getJSON callback:
$(document).ready(function() {
$(document).bind('IssuesReceived', IssuesReceived)
$.getJSON("url", function(data) {
$(document).trigger('IssuesReceived', data);
});
});
function IssuesReceived(evt, data) {
// Do something with data here.
}
Update:
Or, you could store the data globally and just use the custom event for notification that the data had been received and the global variable updated.
$(document).ready(function() {
$(document).bind('IssuesReceived', IssuesReceived)
$.getJSON("url", function(data) {
// I prefer the window.data syntax so that it's obvious
// that the variable is global.
window.data = data;
$(document).trigger('IssuesReceived');
});
});
function IssuesReceived(evt) {
// Do something with window.data here.
// (e.g. create the drag 'n drop interface)
}
// Wired up as the "drop" callback handler on
// your drag 'n drop UI.
function OnDrop(evt) {
// Modify window.data accordingly.
}
// Maybe wired up as the click handler for a
// "Save changes" button.
function SaveChanges() {
$.post("SaveUrl", window.data);
}
Update 2:
In response to this:
Does anyone have an idea how I should be blocking user interaction while this is going on? Why is it such a concern? Thanks again for all the responses.
The reason that you should avoid blocking the browser with synchronous AJAX calls is that a blocked JavaScript thread blocks everything else in the browser too, including other tabs and even other windows. That means no scrolling, no navigation, no nothing. For all intents and purposes, it appears as though the browser has crashed. As you can imagine, a page that behaves this way is a significant nuisance to its users.
maybe this work, works to me.. :)
$variable= new array();
$.getJSON("url", function(data){
asignVariable(data);
}
function asignVariable(data){
$variable = data;
}
console.log($variable);
Hope it help you..
:)
You could approach this with promises:
var jsonPromise = $.getJSON("url")
jsonPromise.done(function(data) {
// success
// do stuff with data
});
jsonPromise.fail(function(reason) {
// it failed... handle it
});
// other stuff ....
jsonPromise.then(function(data) {
// do moar stuff with data
// will perhaps fire instantly, since the deferred may already be resolved.
});
It is pretty straight forward and a viable way to make async code feel more imperative.
"But this is done via subsequent interactions on the page, and I cannot foresee what the user will want to do with the JSON object within the callback."
The callback is your opportunity to set the screen up for the user's interaction with the data.
You can create or reveal HTML for the user, and set up more callbacks.
Most of the time, none of your code will be running. Programming an Ajax page is all about thinking about which events might happen when.
There's a reason it's "Ajax" and not "Sjax." There's a reason it's a pain to change from async to sync. It's expected you'll do the page async.
Event-driven programming can be frustrating at first.
I've done computationally intensive financial algorithms in JS. Even then, it's the same thing--you break it up into little parts, and the events are timeouts.
Animation in JavaScript is also event driven. In fact, the browser won't even show the movement unless your script relinquishes control repeatedly.
You are just running into scoping issues.
Short answer:
window.jsonIssues = {}; // or tack on to some other accessible var
$.getJSON("url", function(data) {
window.jsonIssues = data.Issues;
});
// see results here
alert(window.jsonIssues);
Long answers:
Scoping issue in Javascript
Javascript closure scoping issue

Categories

Resources