I have a simple function like this that has a async function inside
of it:
var b = function(response) {
response = '';
async_function(function (response) {
response = 'test';
});
}
I want to test function b to see if it sets the value of response
to "test". If not, it should raise an error. I do not want to add a parameter
for callback function for b and I do not want to wait using setTimeOut().
What is the correct way to test b using mocha (and chai)?
Thanks you!
var b = function(response) {
response = '';
async_function(function (response) {
response = 'test';
});
}
You cant know when response will be set.
Unless you use setTimeout you cant test it,therefore you should refactor your code.
Related
This question already has answers here:
Why is my variable unaltered after I modify it inside of a function? - Asynchronous code reference
(7 answers)
Closed 7 years ago.
var test = null;
$.get("/zeWebsite.php?blarg=421", function(data){
test = data.trim();
});
alert(test);
I always thought declaring the variable from outside the function before hand gave access to the variable even if called within the function. However, my test variable is still alerting as null.
It is likely an order issue as the $.get function is asynchronous. The callback of the get is not being run until after the alert has already fired.
The alert is being run BEFORE the get has completed. You need to make sure the get is complete before alerting by triggering alert within the callback.
Try:
var test = null;
$.get("/zeWebsite.php?blarg=421", function(data){
test = data.trim();
alert(test);
});
or
var test = null;
$.get("/zeWebsite.php?blarg=421", function(data){
test = data.trim();
outside();
});
function outside() { alert(test); }
it just not assigned yet..
You can simple wait for it:
var test = null, inprocess = true;
$.get("/zeWebsite.php?blarg=421", function(data){
test = data.trim();
inprocess = false;
});
var wait = setInterval(function(){
if (!inprocess) {
clearInterval(wait);
alert(test);
}
}, 500);
But this code is awful. Much better to trigger callback directly:
var test = null;
$.get("/zeWebsite.php?blarg=421", function(data){
test = data.trim();
anyactionwithtest(test);
alert(test);
});
Or use something like jquery deffered promise:
var test = null;
$.get("/zeWebsite.php?blarg=421", function(data){
test = data.trim();
}).done(function (data){
// data also accessible here as is
alert(test);
});
This is because you are using the variable before the get statement execute completely.
Test will get the value when the get function callback call.
If you place alert in get function then it will call at last.
I am trying to use jQuery's AJAX deferreds to return a JSON string that can be parsed and used but I have coded myself into a corner and may have screwed up the logic. I expected the results of the AJAX call to be returned in the .done() callback and they are. I thought once done that I could return the result for use in the remainder of the function. I know that I'm missing something really obvious and simple, I just cannot put a finger on what it is.
Here is the initial coding of the function, stil very much in test mode. The JSON is correctly returned in the .done() function but I cannot assign it outside of the function for use.
checkUserRoles = function(){
var userRole, foo, roles, i$, len$, i;
userRole = roleChecker();
foo = userRole.done(function(data){
var bar;
bar = foo.responseText; // correctly returns the JSON data
console.log(bar);
return bar; // is undefined, this is the problem
});
if (userRole !== false) {
userRole = jsonDecode(userRole);
} else {
userRole = "";
}
roles = userRole.split(',');
$("if-user-role").hide();
for (i$ = 0, len$ = roles.length; i$ < len$; ++i$) {
i = roles[i$];
$("if-user-role[data-role~=" + i + "]").show();
}
return true;
};
this.roleChecker = function(){
var retVal, allCookies, i$, len$, thecookie, hashedCookie, theHash, userHash, post;
retVal = "";
allCookies = document.cookie.split(';');
for (i$ = 0, len$ = allCookies.length; i$ < len$; ++i$) {
thecookie = allCookies[i$];
if (thecookie.indexOf('userHash') >= 0) {
hashedCookie = thecookie;
theHash = hashedCookie.split('=');
userHash = theHash[1];
}
}
post = $.ajax({
url: '/services/check_hash.php',
data: {
hash: userHash
},
type: "POST"
});
return post;
};
The code that you see here is the output from the compiling of LiveScript which we use extensively. I don't think the LiveScript is having an effect on the final result, I just had to do a lot to get what I expected would be the proper JavaScript / jQuery output.
NOTE: because this is more or less the first pass at the code foo doesn't get passed along to the subsequent if statement as userRole was originally hard-coded for the initial testing prior to trying to make the function more dynamic.
How do I return foo.responseText or bar for use in the subsequent procedure? Do I need to put the procedure, beginning with the if conditional in the .done() function?
You're looking for .then and not .done.
What .done does is perform an action and return the same promise. On the other hand then returns a new promise which is resolved with the return value of the callback provided to it. (Assuming the $.ajax resolved correctly).
You of course then need to place everything you subsequently do in the chain:
userRole.then(function(data){
var bar;
bar = foo.responseText; // correctly returns the JSON data
console.log(bar);
return bar;
}).then(function(role){;
if (role != false) {
role = jsonDecode(userRole);
} else {
userRole = "";
}
//...
return true;
});
You should also return that promise to hook on it later.
It looks like you are using deferred objects synchronously, which (as you mentioned in your title) is not the intended purpose. The code that you process the user data with after .done() will execute immediately after registering that handler, so your data won't be ready yet.
When you register a .then() on a deferred promise, you're telling your program to run a piece of code after the deferred object has either resolved or rejected. The program will not wait until that deferred object has resolved or rejected, it will continue processing code (which is the beauty of the deferred object system!)
Example:
var checkUserRoles = function () {
var userRole = roleChecker();
// go off and find the user data
// but *don't wait for it before continuing code execution*
userRole.then(function(data){
// at this point the AJAX request has finished, and we have the data
console.log(data);
// put code to process the user data here
});
// code here is executed immediately after registering the .then handler
// so the user data has not loaded yet
};
var roleChecker = function () {
var defer = $.Deferred();
var post = defer.promise();
// simulate an AJAX Request, returns after 2 seconds
setTimeout(function () {
defer.resolve('user data here!');
}, 2000);
return post;
};
checkUserRoles();
I'm trying to figure out a good way to check if some asynchronous call is "ready" or not. I have some function that runs $.ajax, and in the callback function sets a boolean variable in the global scope to true (along with some other stuff). Before the ajax call, that boolean variable is false.
I want another function that retrieves that "other stuff." Since the ajax call is asynchronous, clearly I can't just immediately go retrieving it because it probably won't be there yet. That's where this boolean variable comes in. I'm thinking I can just check if that boolean is true every 100ms, and when it is, THEN go retrieve and return the "other stuff".
Code-wise, it looks something like this:
window.FOO = window.FOO || {};
;(function() {
var isReady = false;
var stuff;
$.ajax({
...
success: function(data) {
stuff = data;
isReady = true;
}
})
FOO.getStuff = function() {
// How to check if it's "ready"?
};
}
... (somewhere else)...
var stuff = FOO.getStuff();
I've tried the following for FOO.getStuff to no avail, because (I think) setTimeout is asynchronous:
FOO.getStuff = function() {
if (isReady) {
return stuff;
}
var theStuff;
setTimeout(function() {
theStuff = FOO.getStuff();
}, 100);
return theStuff;
};
Using the console, I can see it doing the right thing... but the first FOO.getStuff call returns before the subsequent ones do.
Is there a better way of doing this?
Edit: To clarify, I want the ajax call to remain asynchronous. I'm perfectly fine with the getStuff() calls being synchronous, because the ajax call will be very fast, and in most cases, getStuff() will be called later (after the used does some things).
Per your comments I have your answer. To solve async problem we should do async actions.
var stuff;
var stuffQueue = [];
$.ajax({
success: function(data) {
stuff = data;
if( stuffQueue.length > 0 ){
for(var i in stuffQueue){
var callback = stuffQueue[i];
callback(stuff);
}
stuffQueue = [];
}
}
});
function getStuff(callback){
//stuff has been loaded?
if( stuff ){
callback(stuff);
}else{
stuffQueue.push(callback);
}
}
To get stuff invoke:
var something = getStuff(function(stuff){
console.log(stuff);
});
This should solve your use case. Let me tell you more info, I have a JavaScript template engine, not yet open source but I have been using in professional projects, and I load all the templates with just one HTTP request, I made that request async:false because it is a dependence of the project.
There are reads that said that async false is evil, I do not believe so, what is evil is to use it wrong. Loading a templates file master, is a good example where async:false could work.
Additional I recommend you to read about promisses:
http://www.html5rocks.com/en/tutorials/es6/promises/
similar idea with Danie Aranda, I'd like to sugget you use custom event.
var isReady = true;
$.ajax({
beforeSend: function() {
isReady = false;
},
success: function(data) {
isReady = true;
$(document).trigger('display-stuff', data);
}
});
Foo.getStuff = function(data) {
if (!isReady) {
$(document).one('display-stuff', Foo.getStuff);
return;
}
// do something
};
I have one simple question, been searching on Stack Overflow there are some questions on this topic but can't get a working solution.
I have a simple function for getting number of page likes on Javascript SDK:
function getLikes(div, graf) {
var numblike;
FB.api(graf, function(response) {
var numblike = response.likes;
$(div).prepend(numblike);
});
return numblike; // can't get it to return
}
var pLike = getLikes ("#mydiv", /app_id); // always undefined
Function works and it pre-pends the right number to my div but return always sets my variable to undefined. I understand that the script runs asynchronous and I need to use a callback function but I just can't get it right.
This is called javascript event loop. you can't return numblike from the function cause it's set only in the callback of FB.api.
So you can do similar to that - just send callback:
function getLikes(div, graf,callback) {
FB.api(graf, function(response) {
var numblike = response.likes;
$(div).prepend(numblike);
callback(numblike);
});
}
getLikes ("#mydiv", /app_id,function(numblike){
alert(numblike);
}); // always undefined
You have decalred numblike two times, just remove var declaration in the second one:
function getLikes(div, graf) {
var numblike;
FB.api(graf, function(response) {
numblike = response.likes; // var removed
$(div).prepend(numblike);
});
return numblike;
}
I'm probably missing something simple but given this JS code:
var WS = {
whoami: function () {
var toReturn;
$.getJSON("/SecurityData/GetCurrentUser", function (data) {
toReturn = data.Email;
});
return toReturn;
}
}
When I call it, if I put a breakpoint on the toReturn = data.Email, the expected data is there but if don't WS.whoami is undefined.
I assume this is because the $.getJSON call is async, but how can I get the desired effect?
Ajax is asynchronous and returns a promise object. Instead, return the promise object and add a callback to it.
var WS = {
whoami: function () {
return $.getJSON("/SecurityData/GetCurrentUser");
}
};
WS.whoami().done(function(data){
alert(data.Email);
});
The only other option would be to make it a synchronous request, however I do not recommend it due to the impact it will have on your UX. You would have to use $.ajax and async:false
A better solution would be to call your function with a callback. This way, your code stays async, and continues when the json call is complete.
var WS = {
whoami: function (callback) {
$.getJSON("/SecurityData/GetCurrentUser", callback);
}
}
WS.whoami(function(data) {
// code that uses var data
});