While I'm generally comfortable with rudimentary asynch patterns in node I have come across a situation where I need the return value from an asynch call in a generally synchronous situation.
Given the function:
modelHelper.saveFile = function(field) {
var url = cloudinary.uploader.upload(this.req.files[field.name].path, function(result) {
if(typeof result.url != "undefined" && result.url.length > 0)
{
console.log(" \n\n\n Inside the cloudinary call.");
console.log("\n\n URL = " + result.url);
return result.url;
}
return false;
});
console.log("\n\n\n Outside the load, URL =" + url);
if(!url) return "";
return url;
};
This function is called in the case of uploading a file to a server, and is called by a simple loop which loops over all of the elements of a page. For the majority of cases it's a simple mapping of variable to value, as such I really don't want to need to inject a next into this function.
Here's the caller:
modelHelper.parseField = function(field) {
var type = field.type;
switch(type) {
case "email":
case "url":
return strings.exists(this.param(field.name)) ?
this.param(field.name) : "";
break;
case "file":
return modelHelper.resolveFile.bind(this)(field);
break;
default:
return strings.exists(this.param(field.name)) ?
strings.makeSafe(this.param(field.name)) : "";
}
and this, in turn is called by:
modelHelper.populate = function(elements, record, next){
var len = elements.length;
parseField = modelHelper.parseField.bind(this);
while(len--)
if((elements[len]["type"] != "captcha"))
this[record][elements[len]['name']] = parseField(elements[len]);
next.bind(this)();
};
as such, I'm looking for a pattern that will block execution until the file operation in saveFile returns a variable; something like wrapping it in a setInterval type call was my first thought, but is there a better way?
Pass a callback to saveFile and call it with the url once the upload is complete
modelHelper.saveFile = function(field, callback) {
var url = cloudinary.uploader.upload(this.req.files[field.name].path, function(result) {
if(typeof result.url != "undefined" && result.url.length > 0) {
console.log(" \n\n\n Inside the cloudinary call.");
console.log("\n\n URL = " + result.url);
return callback(null, result.url);
}
return callback();
});
};
Related
I have the following perfectly working code (written by someone else). The problem is that if I simply rename the attr method, I get an error. For example, I rename the method to attrx and get this error:
TypeError: arg.attrx is not a function
Here is the working code:
function Action(name) {
this.attr = function(n) {
if (n=="name") {
return "action";
}
},
this.val = function() {
return name;
};
}
Action.prototype.toString = function() {
return "&" + this.attr("name") + "=" + this.val();
}
When a user triggers an event, the following function is called:
function serializeElements() {
var result = "";
for(var i = 0; i < arguments.length; i++) {
var arg = arguments[i];
result += (arg.attr("name") + "=" + arg.val() + "&");
}
return result;
}
Here is the identical code above but it has the attr method renamed to attrx:
function Action(name) {
this.attrx = function(n) {
if (n=="name") {
return "action";
}
},
this.val = function() {
return name;
};
}
Action.prototype.toString = function() {
return "&" + this.attrx("name") + "=" + this.val();
}
function serializeElements() {
var result = "";
for(var i = 0; i < arguments.length; i++) {
var arg = arguments[i];
result += (arg.attrx("name") + "=" + arg.val() + "&");
}
return result;
}
I cannot figure out the reason that the code does not work (see error at top of message) after I rename the method to attrx or anything else for that matter.
Note: The web page does include jQuery, but I don't think that is what causes the problem.
Here is the code used to call serializeElements:
function addStatesListener() {
$("#states").on("change", function(e) {
var form = $(this.form);
var url = form.attr("action");
var type = form.attr("method");
// pass 1) jQuery 'country' and 'state' objects and 2) a query string fragment, e.g.: '&action=change_state'
var data = serializeElements($("#countries"), $("#states"), new Action("change_state"));
e.preventDefault();
$.ajax({
url: url,
type: type,
data: data,
dataType: "html", // The type of data that you're expecting back from the server
success: function(result) {
$("#cities").html(result); // list of all cities, e.g.: <option value="Albany"</option>
}
});
});
}
The proper answer to your question is want already catch #dinesh, you are passing 3 arguments to your function, and only the third is an Action with the .attrx method you changed.
Considering you are working on jquery objects, and if you want to clean your code, you could use .serialize() method instead of calling the couple .attr() and .val().
.serialize() is the jquery method to serialize form objects.
So you can change your code as follow:
function Action(name) {
this.serialize=function() {
return 'name='+encodeURI(name);
}
}
And then your function serializeElements:
function serializeElements() {
var args = Array.prototype.slice.call(arguments);
return args.reduce(function(a, b){
if (a) return a.serialize() + '&' + b.serialize();
if (b) return b.serialize();
});
}
Then you can call it so:
var data = serializeElements($("#countries,#states"), new Action("change_state"));
As you see, you could put form elements in a comma separated list on jquery selector.
That's it.
I want Ajax to get information while the information is valid. So if I do
$(document).ready(function () {
var url = '/my/url/';
var ses = true;
var i = 0;
while (ses) {
i++;
var temp;
$.get(url + "?get=" + i, function (data) {
if (data != '') {
temp = data;
sortArticles(data);
}
});
if(temp == '') ses = false;
}
});
If I do this without while (putting 0 instead of get var), I get the information I need, but if I put it like this, I enter an infinite loop and the page breaks. By the way, I tested and the if(data != '') statement works as intended.
I don't know why temp doesn't change the state of the ses variable. I tried putting an else statement inside $.get(..)
else ses = false;
but it doesn't do the trick neither.
Thanks for reading!
jQuery ajax (as most ajax implementations), which $.get() is a shorthand for, is asynchronouos be default. The callback function is only executed once the request is complete. The while continues indefinitely since without yielding control out to the JS engine the callback doesn't get a chance to do it's work - even if the request(s) are done.
In order to continuously send requests, try like this:
function requestMore(i) {
$.get('/my/url/?get=' + i, function (data) {
if (data != '') {
sortArticles(data);
requestMore(i + 1);
}
});
}
(document).ready(function () {
requestMore(1);
});
The way you wrote your code temp will never be ''.
The problem is temp is not a string. It's undefined.
Instead of:
if(temp == '') ses = false;
write:
if(typeof(temp) == 'undefined') ses = false;
$(document).ready(function () {
var url = '/my/url/';
var ses = true;
var i = 0;
while (ses) {
i++;
var temp='';
$.get(url + "?get=" + i, function (data) {
if (data != '') {
temp = data;
sortArticles(data);
}
});
if(temp == '') ses = false;
}
});
This will work.Initialize temp as a blank string as shown :)
i am trying to call function in other js file using require.all these the parameters
> Login = require("LoginRequest");
Create = require("CreateView");
var params = { username : username.value , password : password.value};
var type ="POST";
var URL = "https://localhost/post_auth.php";
var Result; </li>
and here the call funcion from LoginScreen.js
b2.addEventListener('click',function(e)
{
alert(params.username);
if (username.value != '' && password.value != '')
{
Result=Login.Request(params,type,URL);
}
else
{
// warning alert
alert("Username/Password are required");
}
if (Result.logged == true)
{
alert("Welcome " + Result.name + ", Please answer the following question");
Create();
}
else
{
alert(Result.message);
}
});
when i try to pass the parameters to LoginRequest.
function Request(params,type,url){
var Result;
var loginReq = Titanium.Network.createHTTPClient();
loginReq.open(type,url);
loginReq.setRequestHeader("Content-Type", "application/json; charset=utf-8");
//send parameters
loginReq.send(JSON.stringify(params));
loginReq.onload = function()
{
var json = this.responseText;
Result = JSON.parse(json);
alert (Result.logged);
alert (Result.name);
};
return Result;
};
exports.Request = Request;
the calling return undifiend object , where is my wrong here ?
That's because you are making an async call.
when you call loginReq.send() the call will be made and it will continue executing the rest of the code without waiting for the async call to be finished, that's why the function returns undefined.
To fix this you can make a sync call instead of an async call (this is a bad bad bad idea) or you could restructure your code, maybe LoginRequest could return the loginReq instance instead of the result
I have json data with "tagged" values (from a jsonp source):
{"foo": "#duration:8542"}
which I can parse on-the-fly by passing a function as the second argument to JSON.parse:
dk.json = {
parse: function (s) {
return JSON.parse(s, function (key, val) {
if (typeof val === 'string' && val[0] === '#') {
var colonpos = val.indexOf(':');
if (colonpos > 1) {
var tag = val.slice(0, colonpos + 1);
switch (tag) {
case dk.Date.tag: return dk.Date.create(val);
case dk.Duration.tag: return dk.Duration.create(val);
}
}
}
return val;
});
},
//...
};
but how can I plug this parsing function into jQuery.ajax()? Something more sensible than:
success: function (data) {
data = dk.json.parse(JSON.stringify(data));
...
dataFilter, and especially converters looked promising:
$.ajax({
dataType: 'jsonp',
converters: {
'text json': dk.json.parse
},
// ...
});
but that doesn't get called at all (dataFilter gets called, but with the data parameter set to undefined).
Where am I going wrong?
[Edit:]
I know I can write a traversal function that walks the JSON object returned by jQuery, eg:
function untag(val) {
if (typeof val === 'string' && val[0] === '#') {
var colonpos = val.indexOf(':');
if (colonpos > 1) {
var tag = val.slice(0, colonpos + 1);
switch (tag) {
case dk.Date.tag: return dk.Date.create(val);
case dk.Duration.tag: return dk.Duration.create(val);
}
}
}
return val;
}
var untag_json = function (jsonobj) {
var _traverse = function _traverse(obj, result) {
var value;
for (var attr in obj) {
value = obj[attr];
if (value && typeof value === 'object') {
result[attr] = _traverse(value, {});
} else {
result[attr] = untag(value);
}
}
return result;
};
return _traverse(jsonobj, {});
};
and then call it in the success handler:
success: function (data) {
data = untag_json(data);
...
but that seems like a lot of unnecessary work.. Is there no way to use the converters parameter to $.ajax to get access to the unparsed (i.e. text) json source?
There actually isn't any JSON parsing in a JSONP request (src), which can seem counter intuitive. What is happening is the string that is returning from the JSONP endpoint is evaluated as JavaScript (with a reference to a function that is defined (or added in dynamically) in the DOM making the JSONP request like this:
_callback({'foo':'#duration:8524'});
If you wanted to use your function you would need to make the endpoint return a String like this:
_callback("{'foo':'#duration:8524'}");
then in the JSONP callback you could call JSON.parse(). JSON parse is a rather safe way to process JSON so if this was easier to reason about then it would be a fine approach.
Hi you need to set this header application/json in the response from server side then you can simply set dataType:json or dataType:jsonp then you will not need to stringify or parse the json. You then just get objects, properties or arrays from json.
For example : in php we use
$json_string = "{"foo": "#duration:8542"}";
$json = json_decode($json_string);
$foo = $json->foo;
echo $foo;//prints #duration:8542
In jquery you can do this:
sucess:function(response) {
var foo = response.foo;
console.log(foo);
}
Hope this helps
Is it possible to get a reference to the triggerElement that invoked the Ajax request in the onSuccess function?
<%=Ajax.ActionLink("x", a, r, New AjaxOptions With {.OnSuccess = _
"function(context) {alert('get triggerElement reference here?');}" })%>
The page is rendered to:
x
So let's have a look at Sys.Mvc.AsyncHyperlink.handleClick inside Scripts\MicrosoftMvcAjax.debug.js:
Sys.Mvc.AsyncHyperlink.handleClick = function Sys_Mvc_AsyncHyperlink$handleClick(anchor, evt, ajaxOptions) {
/// omitted doc comments
evt.preventDefault();
Sys.Mvc.MvcHelpers._asyncRequest(anchor.href, 'post', '', anchor, ajaxOptions);
}
So the ActionLink is rendered to an anchor ("a") tag, with an "onclick" event, which uses Sys.Mvc.AsyncHyperlink.handleClick with this as one of the parameters, mapped to anchor.
Then there's this Sys.Mvc.MvcHelpers._asyncRequest call with anchor as the fourth parameter. Let's have a look in Sys.Mvc.MvcHelpers._asyncRequest:
Sys.Mvc.MvcHelpers._asyncRequest = function Sys_Mvc_MvcHelpers$_asyncRequest(url, verb, body, triggerElement, ajaxOptions) {
/// omitted documentation
if (ajaxOptions.confirm) {
if (!confirm(ajaxOptions.confirm)) {
return;
}
}
if (ajaxOptions.url) {
url = ajaxOptions.url;
}
if (ajaxOptions.httpMethod) {
verb = ajaxOptions.httpMethod;
}
if (body.length > 0 && !body.endsWith('&')) {
body += '&';
}
body += 'X-Requested-With=XMLHttpRequest';
var requestBody = '';
if (verb.toUpperCase() === 'GET' || verb.toUpperCase() === 'DELETE') {
if (url.indexOf('?') > -1) {
if (!url.endsWith('&')) {
url += '&';
}
url += body;
}
else {
url += '?';
url += body;
}
}
else {
requestBody = body;
}
var request = new Sys.Net.WebRequest();
request.set_url(url);
request.set_httpVerb(verb);
request.set_body(requestBody);
if (verb.toUpperCase() === 'PUT') {
request.get_headers()['Content-Type'] = 'application/x-www-form-urlencoded;';
}
request.get_headers()['X-Requested-With'] = 'XMLHttpRequest';
var updateElement = null;
if (ajaxOptions.updateTargetId) {
updateElement = $get(ajaxOptions.updateTargetId);
}
var loadingElement = null;
if (ajaxOptions.loadingElementId) {
loadingElement = $get(ajaxOptions.loadingElementId);
}
var ajaxContext = new Sys.Mvc.AjaxContext(request, updateElement, loadingElement, ajaxOptions.insertionMode);
var continueRequest = true;
if (ajaxOptions.onBegin) {
continueRequest = ajaxOptions.onBegin(ajaxContext) !== false;
}
if (loadingElement) {
Sys.UI.DomElement.setVisible(ajaxContext.get_loadingElement(), true);
}
if (continueRequest) {
request.add_completed(Function.createDelegate(null, function(executor) {
Sys.Mvc.MvcHelpers._onComplete(request, ajaxOptions, ajaxContext);
}));
request.invoke();
}
}
So the original anchor is now triggerElement, but as you can see, this parameter is never used in the function's body.
So, if you want to have some kind of a "formal" (or documented) reference to triggerElement - no such thing.
But hey, it's JavaScript, so you can access almost anything as long as the browser did not move to another page, including the call stack. For instance:
<script type="text/javascript">
function a(p, q)
{
b();
}
function b() {
var x = arguments.caller[1];
alert(x); // boo!
}
a(789, "boo!");
</script>
So eventually you can hack it and access the original anchor. I suggest you do the following:
Write a function to be invoked in the
OnBegin.
Inside this function, access
the original triggerElement, and
add it as a property to the original
ajaxOptions (which can be accessed,
too)
Then, in the OnSuccess function,
access your hacked property of
ajaxOptions.