By using this site, you agree to our updated Privacy Policy and our Terms of Use. Manage your Cookies Settings.
434,741 Members | 2,033 Online
Bytes IT Community
+ Ask a Question
Need help? Post your question and get tips & solutions from a community of 434,741 IT Pros & Developers. It's quick & easy.

Dynamically generated functions with variable-based payload

P: n/a

I'm trying to generate dynamic functions to use as separate
callbacks for an AJAX API call.

The API doesn't seem to allow for the inclusion of any parameters
in the callback, so I can't associate the call with the handling
routine (for reference purposes, I'm calling the Google Language
Translation API). As a work-around, I thought I'd dynamically
generate a unique callback function for each API call.

Right now, I'm stuck hobbling the API calls by running them
synchronously, since they don't seem to allow parameterizing the
callback function. This rather slows things down.

Of course, since I haven't gotten my work around running, I can't
prove that it will have the desired effect of parellizing the API
calls for me.
I'm running into what are probably some fairly basic issues when
generating the function definitions (and probably the initial calling
API code, but since I haven't gotten that far, I'm not sure yet).

For example:

for (i = 0; i < 10; i++) {
var text = 'Test [' + eval( i ) + ']';
window[ 'alerter' + i ] = function () { alert ( text ); }
}

This code generates my ten functions (alerter0(), alerter1(),
alerter2(), etc) but the body of the functions seems to be getting the
variable reference to 'i', instead of the string literal value of 'i'
during that loop iteration. So when I execute any of the 'alerterX()'
functions, they /all/ pop up alert boxes using the referenced value of
'i' at the end of the loop, instead of the intended effect where
'alerter0()' would have the text 'Test [0]', 'alerter1()' would have
the text 'Test [1]', etc.

The goal is to build an arbitrary number of unique callback
functions of the model:

// myelements[] is a global array of html elements where the text is
being replaced with the returned value
function callBackXX(result) {
if (result.translation) {
myelements[XX].innerHTML = result.translation;
} else {
myelements[XX].innerHTML = '';
}
}

With calls along the model of:

google.language.translate(text, 'en', 'es', callBack0 );

google.language.translate(text, 'en', 'es', callBack1 );

google.language.translate(text, 'en', 'es', callBack2 );

With these calls, obviously, being dynamically generated in the
loop as well.
Any pointers on how to generate the required static strings for
the function bodies, instead of getting variable references would be
appreciated. I'd like to avoid using eval() (Yes, I know my example
used it!), as well.

Thanks in advance.

-Joe
Sep 25 '08 #1
Share this Question
Share on Google+
9 Replies


P: n/a
Dahak wrote:
I'm trying to generate dynamic functions to use as separate
callbacks for an AJAX API call.

The API doesn't seem to allow for the inclusion of any parameters
in the callback, so I can't associate the call with the handling
routine (for reference purposes, I'm calling the Google Language
Translation API). As a work-around, I thought I'd dynamically
generate a unique callback function for each API call.
Use an object.
Right now, I'm stuck hobbling the API calls by running them
synchronously, since they don't seem to allow parameterizing the
callback function. This rather slows things down.
Bad idea.
>
Of course, since I haven't gotten my work around running, I can't
prove that it will have the desired effect of parellizing the API
calls for me.
I'm running into what are probably some fairly basic issues when
generating the function definitions (and probably the initial calling
API code, but since I haven't gotten that far, I'm not sure yet).

For example:

for (i = 0; i < 10; i++) {
var text = 'Test [' + eval( i ) + ']';
window[ 'alerter' + i ] = function () { alert ( text ); }
}

This eval:

eval( i )

- is completely useless. The approach is not going to work, so it's
almost not even relevant because you'll be scrapping it anyway, but in
the future, don't use eval. The way you used it here is only going to
add inefficiency.

This code generates my ten functions (alerter0(), alerter1(),
alerter2(), etc) but the body of the functions seems to be getting the
variable reference to 'i', instead of the string literal value of 'i'
during that loop iteration. So when I execute any of the 'alerterX()'
functions, they /all/ pop up alert boxes using the referenced value of
'i' at the end of the loop, instead of the intended effect where
'alerter0()' would have the text 'Test [0]', 'alerter1()' would have
the text 'Test [1]', etc.
Why is that?

Here's what's happening:

You have created 10 properties on the window object, window['alerter' +
i] points to a unique function. Each function has the same FunctionBody
code and scope. They're essentially equivalent functions, but different
objects*.

So, window.alerter1() will find and then call the window's |alerter1|
property. Function |alerter1|'s function body has a reference to the
identifier |text|.

That identifier is resolved in the containing scope. This resolution
happens when the function is called, after the loop has terminated.
After the loop is completed, |text| has the value "Text [9]".
* No implementation appears to support joined functions.
The goal is to build an arbitrary number of unique callback
functions of the model:

// myelements[] is a global array of html elements where the text is
being replaced with the returned value
function callBackXX(result) {
if (result.translation) {
myelements[XX].innerHTML = result.translation;
} else {
myelements[XX].innerHTML = '';
}
}

With calls along the model of:

google.language.translate(text, 'en', 'es', callBack0 );

google.language.translate(text, 'en', 'es', callBack1 );

google.language.translate(text, 'en', 'es', callBack2 );

With these calls, obviously, being dynamically generated in the
loop as well.
In the case of passing methods as callbacks, you'll often want to pass
in a context argument.

You can use a closure to resolve the context.
>
Any pointers on how to generate the required static strings for
the function bodies, instead of getting variable references would be
appreciated. I'd like to avoid using eval() (Yes, I know my example
used it!), as well.
Custom FunctionBody can be achieved with the Function constructor. Do
not do this.

It looks like you're using a service to get translated text. I don't
know what this has to do with generating unique FunctionBody.

function AcmeAbominator(i) {
this.i = i;
}
AcmeAbominator.instances = [];

AcmeAbominator.prototype.abmoniate = function() {
alert('Abominating Road Runner ' + this.i);
};

for(var i = 0; i < 10; i++) {
AcmeAbominator.instances[i] = new AcmeAbominator( i );
}
Garrett
Thanks in advance.

-Joe
Sep 25 '08 #2

P: n/a
On Sep 25, 11:15 am, Dahak <Dahak...@theXOUTfifthimperium.com.invalid>
wrote:
<snip>
I'm running into what are probably some fairly basic issues when
generating the function definitions (and probably the initial calling
API code, but since I haven't gotten that far, I'm not sure yet).

For example:

for (i = 0; i < 10; i++) {
var text = 'Test [' + eval( i ) + ']';
window[ 'alerter' + i ] = function () { alert ( text ); }
}
You seem to misunderstand how closures work. There are several ways to
correctly use closures to do this. The easiest to grok is probably to
use a "factory" function that generates the function you want:

function makeAlerter (text) {
return function () {
alert(text);
}
}
for (var i=0;i<10;i++) {
window['alerter'+i] = makeAlerter('Test['+i+']');
}

Once you realise what's happening with the code above you'll realise
that you can also code it like this:

for (var i=0;i<10;i++) {
window['alerter'+i] = (function(text){
return function () {
alert(text);
}
})('Test['+i+']')
}

And once you realise that you can use closures with anonymous function
you realise you don't need to create named functions at all to pass to
the callback:

google.language.translate(text, 'en', 'es', function (result) {
if (result.translation) {
myelements[0].innerHTML = result.translation;
}
else {
myelements[0].innerHTML = result.translation;
}
});

Or better yet, encapsulate the logic into a factory function:

function makeCallback (htmlElement) {
return function (result) {
if (result.translation) {
htmlElement.innerHTML = result.translation;
}
else {
htmlElement.innerHTML = result.translation;
}
}
}
google.language.translate(text,'en','es',makeCallb ack(myelements[0]));
google.language.translate(text,'en','es',makeCallb ack(myelements[1]));
google.language.translate(text,'en','es',makeCallb ack(myelements[2]));
Sep 25 '08 #3

P: n/a
On Sep 25, 8:15 am, Dahak <Dahak...@theXOUTfifthimperium.com.invalid>
wrote:
I'm trying to generate dynamic functions to use as separate
You can try the object oriented way:

//class MyTranslationDiv
function MyTranslationDiv(elemId){
this.div = document.getElementById(elemId);
}

MyTranslationDiv.prototype.translate = function(result){
if (!result.error) {
this.div.innerHTML = result.translation;
}else{
this.div.innerHTML = "Error happened";
}
}

//instance of MyTranslationDiv
var myTranDiv = new MyTranslationDiv("translation");
google.language.translate(
"have a nice day", "en", "fr",
function(result){ myTranDiv.translate(result) }
);

- Kiran Makam
Sep 25 '08 #4

P: n/a
On Wed, 24 Sep 2008 21:37:55 -0700, an orbiting mind-control laser
made dhtml <dh**********@gmail.comwrite:
>Dahak wrote:
> Right now, I'm stuck hobbling the API calls by running them
synchronously, since they don't seem to allow parameterizing the
callback function. This rather slows things down.

Bad idea.
Yes, I know... While it works, it stinks, thus the hideous
attempts to work around it.

-SNIP-
>This eval:

eval( i )

- is completely useless. The approach is not going to work, so it's
almost not even relevant because you'll be scrapping it anyway, but in
the future, don't use eval. The way you used it here is only going to
add inefficiency.
Yep. I'll admit that. It's a holdover from earlier attempts to
evaluate the variable. It failed just as badly as simply using the
variable i. Since it didn't fail any worse, I never bothered removing
it as I looked at other things.

-SNIP-
>Why is that?

Here's what's happening:

You have created 10 properties on the window object, window['alerter' +
i] points to a unique function. Each function has the same FunctionBody
code and scope. They're essentially equivalent functions, but different
objects*.

So, window.alerter1() will find and then call the window's |alerter1|
property. Function |alerter1|'s function body has a reference to the
identifier |text|.
Which I already knew... I'm trying to determine how to get it to
resolve as a string literal, not a variable reference.

-SNIP-
>In the case of passing methods as callbacks, you'll often want to pass
in a context argument.

You can use a closure to resolve the context.
-SNIP-
>Custom FunctionBody can be achieved with the Function constructor. Do
not do this.
Why not? I was planning on trying this approach in the morning.
>It looks like you're using a service to get translated text. I don't
know what this has to do with generating unique FunctionBody.
If I had a single string to translate, it would be trivial.

I have an arbitrary number of text strings to translate. The API
evidently (I could be wrong, I'm still trying to determine this) does
not allow the passing of parameters to the callback function. If it
did, my search would be over and I could skip the silliness of unique
callback functions.
-Joe
Sep 25 '08 #5

P: n/a
On Wed, 24 Sep 2008 22:18:07 -0700 (PDT), an orbiting mind-control
laser made slebetman <sl*******@gmail.comwrite:
>On Sep 25, 11:15 am, Dahak <Dahak...@theXOUTfifthimperium.com.invalid>
-SNIP-
>You seem to misunderstand how closures work.
Oh, no doubt. I've never had the need for something like this
before.
There are several ways to
correctly use closures to do this. The easiest to grok is probably to
use a "factory" function that generates the function you want:

function makeAlerter (text) {
return function () {
alert(text);
}
}
That seems pretty straightforward.
>for (var i=0;i<10;i++) {
window['alerter'+i] = makeAlerter('Test['+i+']');
}

Once you realise what's happening with the code above you'll realise
that you can also code it like this:

for (var i=0;i<10;i++) {
window['alerter'+i] = (function(text){
return function () {
alert(text);
}
})('Test['+i+']')
}

And once you realise that you can use closures with anonymous function
you realise you don't need to create named functions at all to pass to
the callback:
Thanks, I'll check into this.
>google.language.translate(text, 'en', 'es', function (result) {
if (result.translation) {
myelements[0].innerHTML = result.translation;
}
else {
myelements[0].innerHTML = result.translation;
}
});

Or better yet, encapsulate the logic into a factory function:

function makeCallback (htmlElement) {
return function (result) {
if (result.translation) {
htmlElement.innerHTML = result.translation;
}
else {
htmlElement.innerHTML = result.translation;
}
}
}
google.language.translate(text,'en','es',makeCall back(myelements[0]));
google.language.translate(text,'en','es',makeCall back(myelements[1]));
google.language.translate(text,'en','es',makeCall back(myelements[2]));
I wasn't able to get my tests to parameterize the callback to
work. If I can pass a reference to the source string through the
callback, I should be able avoid dynamic functions altogether.

Thanks.

-Joe
Sep 25 '08 #6

P: n/a
On Wed, 24 Sep 2008 22:25:27 -0700 (PDT), an orbiting mind-control
laser made Kiran Makam <ki*******@gmail.comwrite:
>On Sep 25, 8:15 am, Dahak <Dahak...@theXOUTfifthimperium.com.invalid>
wrote:
> I'm trying to generate dynamic functions to use as separate

You can try the object oriented way:

//class MyTranslationDiv
function MyTranslationDiv(elemId){
this.div = document.getElementById(elemId);
}
One of the problems I'm currently faced with is that I have to
deal with an arbitrary number of elements on a page. I find all
elements with a class of 'translate', then traverse that array through
the google API call, regrettably, I don't have unique identifiers.

The issue is associating the source element with the return
callback when using the asynchronous API call.

>MyTranslationDiv.prototype.translate = function(result){
if (!result.error) {
this.div.innerHTML = result.translation;
}else{
this.div.innerHTML = "Error happened";
}
}

//instance of MyTranslationDiv
var myTranDiv = new MyTranslationDiv("translation");
google.language.translate(
"have a nice day", "en", "fr",
function(result){ myTranDiv.translate(result) }
);

- Kiran Makam
Thanks for something to test out.

-Joe
Sep 25 '08 #7

P: n/a
On Sep 25, 6:06*pm, Dahak <Dahak...@theXOUTfifthimperium.com.invalid>
wrote:
>
* * * * One of the problems I'm currently faced with is that I have to
deal with an arbitrary number of elements on a page. *I find all
elements with a class of 'translate', then traverse that array through
the google API call, regrettably, I don't have unique identifiers.

* * * * The issue is associating the source element with the return
callback when using the asynchronous API call.
To get all elements having a particular class, you can use
getElementsByClassName (google to find implementations of
getElementsByClassName function, Prototype has one)

code:
---------------
//class MyTranslationDiv
function MyTranslationDiv(objDiv){
this.div = objDiv;
}

MyTranslationDiv.prototype.update = function(result){
if (!result.error) {
this.div.innerHTML = result.translation;
}else{
this.div.innerHTML = "Error happened";
}

}

MyTranslationDiv.prototype.translate = function(textToTranslate){
var thisTmp = this;
google.language.translate(
textToTranslate, "en", "es",
function(result){thisTmp.update(result)}
);

}

//get all elements having className as 'translate'
var elements = getElementsByClassName("translate");

//iterate and translate
for(var i=0, len=elements.length; i<len; i++){
var obj = new MyTranslationDiv( elements[i] );
obj.translate("hello world");
}
----------

- Kiran Makam
Sep 25 '08 #8

P: n/a
On Sep 25, 3:06*pm, Dahak <Dahak...@theXOUTfifthimperium.com.invalid>
wrote:
>
* * * * One of the problems I'm currently faced with is that I have to
deal with an arbitrary number of elements on a page. *I find all
elements with a class of 'translate', then traverse that array through
the google API call, regrettably, I don't have unique identifiers.
See http://jorgechamorro.com/cljs/017/

HTH,
Jorge.

<script>
self.onload= function () {
var d= document, e, i, xhr, url,
t= 'Lorem ipsum dolor sit amet consectetuer adipiscing elit';
t+= 'Pellentesque velit Morbi laoreet lacinia neque Sed eros';
t= t.split(' ');

//Fill the DOM with 'text' tags with className 'translate'
do {
(y('text', e= t.shift(), 1)).className= 'translate';
} while (t.length)

var toTranslate= [];
//poor man's querySelectorAll()
walkTheDOM(d.body, function (node) {
if (node.className === 'translate') { toTranslate.push(node); }
});

//debug();
for (i= 0; i<toTranslate.length; ++i) {
e= toTranslate[i];
t= e.oldValue= e.firstChild.nodeValue;
url= "xhrData.txt?"+t+Math.random();
e.innerHTML= t+': XHR posted: '+url;
xhr= new XMLHttpRequest();
xhr.open("GET", url, true);
xhr.onreadystatechange = (function (xhr, pDOMElement) {
return function () {
var txt= pDOMElement.oldValue;
if (xhr.readyState == 4) {
txt+= ': '+xhr.responseText.substring(0, 28);
} else {
txt+= ": readyState: "+xhr.readyState;
if (xhr.readyState == 3) { txt+= ": RECEIVING." }
}
pDOMElement.innerHTML= txt;
};
})(xhr, e);
xhr.send(null);
}

function y (p, inner, br) {
var e;
if (br) { d.body.appendChild(d.createElement('br')) }
e= d.body.appendChild(d.createElement(p));
if (inner) { e.innerHTML= inner; }
return e;
};

function walkTheDOM (node, f){
f(node);
node= node.firstChild;
while (node) {
walkTheDOM(node, f);
node= node.nextSibling;
}
};
};
</script>
Sep 25 '08 #9

P: n/a
On Thu, 25 Sep 2008 07:25:37 -0700 (PDT), an orbiting mind-control
laser made Kiran Makam <ki*******@gmail.comwrite:

-SNIP-
>To get all elements having a particular class, you can use
getElementsByClassName (google to find implementations of
getElementsByClassName function, Prototype has one)
Thanks. I'd already gotten that aspect running fine. The call to
getElementsByClassName was returning my array of "translate" elements
fine. Though, as I indicated in a previous post, the way my earlier
code was working, I was reduced to forcing the looped API calls to
function synchronously.
>code:
---------------
//class MyTranslationDiv
function MyTranslationDiv(objDiv){
this.div = objDiv;
}

MyTranslationDiv.prototype.update = function(result){
if (!result.error) {
this.div.innerHTML = result.translation;
}else{
this.div.innerHTML = "Error happened";
}

}

MyTranslationDiv.prototype.translate = function(textToTranslate){
var thisTmp = this;
google.language.translate(
textToTranslate, "en", "es",
function(result){thisTmp.update(result)}
);

}

//get all elements having className as 'translate'
var elements = getElementsByClassName("translate");

//iterate and translate
for(var i=0, len=elements.length; i<len; i++){
var obj = new MyTranslationDiv( elements[i] );
obj.translate("hello world");
}
/Very/ nice.

This solved my problem quite neatly. It's cut execution time
(always hostage to network latency) by at least 50%.

Thank you, very much.
>- Kiran Makam
-Joe
Sep 25 '08 #10

This discussion thread is closed

Replies have been disabled for this discussion.