Dynamically generated functions with variable-based payload

Collapse
This topic is closed.
X
X
 
  • Time
  • Show
Clear All
new posts
  • Dahak

    Dynamically generated functions with variable-based payload


    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(resu lt) {
    if (result.transla tion) {
    myelements[XX].innerHTML = result.translat ion;
    } 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
  • dhtml

    #2
    Re: Dynamically generated functions with variable-based payload

    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(resu lt) {
    if (result.transla tion) {
    myelements[XX].innerHTML = result.translat ion;
    } 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.abmon iate = function() {
    alert('Abominat ing Road Runner ' + this.i);
    };

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


    Garrett
    Thanks in advance.
    >
    -Joe

    Comment

    • slebetman

      #3
      Re: Dynamically generated functions with variable-based payload

      On Sep 25, 11:15 am, Dahak <Dahak...@theXO UTfifthimperium .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('Te st['+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.transla tion) {
      myelements[0].innerHTML = result.translat ion;
      }
      else {
      myelements[0].innerHTML = result.translat ion;
      }
      });

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

      function makeCallback (htmlElement) {
      return function (result) {
      if (result.transla tion) {
      htmlElement.inn erHTML = result.translat ion;
      }
      else {
      htmlElement.inn erHTML = result.translat ion;
      }
      }
      }
      google.language .translate(text ,'en','es',make Callback(myelem ents[0]));
      google.language .translate(text ,'en','es',make Callback(myelem ents[1]));
      google.language .translate(text ,'en','es',make Callback(myelem ents[2]));

      Comment

      • Kiran Makam

        #4
        Re: Dynamically generated functions with variable-based payload

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

        //class MyTranslationDi v
        function MyTranslationDi v(elemId){
        this.div = document.getEle mentById(elemId );
        }

        MyTranslationDi v.prototype.tra nslate = function(result ){
        if (!result.error) {
        this.div.innerH TML = result.translat ion;
        }else{
        this.div.innerH TML = "Error happened";
        }
        }

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

        - Kiran Makam

        Comment

        • Dahak

          #5
          Re: Dynamically generated functions with variable-based payload

          On Wed, 24 Sep 2008 21:37:55 -0700, an orbiting mind-control laser
          made dhtml <dhtmlkitchen@g mail.comwrite:
          >Dahak wrote:
          > Right now, I'm stuck hobbling the API calls by running them
          >synchronousl y, 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

          Comment

          • Dahak

            #6
            Re: Dynamically generated functions with variable-based payload

            On Wed, 24 Sep 2008 22:18:07 -0700 (PDT), an orbiting mind-control
            laser made slebetman <slebetman@gmai l.comwrite:
            >On Sep 25, 11:15 am, Dahak <Dahak...@theXO UTfifthimperium .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('Te st['+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.languag e.translate(tex t, 'en', 'es', function (result) {
            if (result.transla tion) {
            myelements[0].innerHTML = result.translat ion;
            }
            else {
            myelements[0].innerHTML = result.translat ion;
            }
            >});
            >
            >Or better yet, encapsulate the logic into a factory function:
            >
            >function makeCallback (htmlElement) {
            return function (result) {
            if (result.transla tion) {
            htmlElement.inn erHTML = result.translat ion;
            }
            else {
            htmlElement.inn erHTML = result.translat ion;
            }
            }
            >}
            >google.languag e.translate(tex t,'en','es',mak eCallback(myele ments[0]));
            >google.languag e.translate(tex t,'en','es',mak eCallback(myele ments[1]));
            >google.languag e.translate(tex t,'en','es',mak eCallback(myele ments[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

            Comment

            • Dahak

              #7
              Re: Dynamically generated functions with variable-based payload

              On Wed, 24 Sep 2008 22:25:27 -0700 (PDT), an orbiting mind-control
              laser made Kiran Makam <kiranmn75@gmai l.comwrite:
              >On Sep 25, 8:15 am, Dahak <Dahak...@theXO UTfifthimperium .com.invalid>
              >wrote:
              > I'm trying to generate dynamic functions to use as separate
              >
              >You can try the object oriented way:
              >
              >//class MyTranslationDi v
              >function MyTranslationDi v(elemId){
              > this.div = document.getEle mentById(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.

              >MyTranslationD iv.prototype.tr anslate = function(result ){
              > if (!result.error) {
              > this.div.innerH TML = result.translat ion;
              > }else{
              > this.div.innerH TML = "Error happened";
              > }
              >}
              >
              >//instance of MyTranslationDi v
              >var myTranDiv = new MyTranslationDi v("translation" );
              >google.languag e.translate(
              > "have a nice day", "en", "fr",
              > function(result ){ myTranDiv.trans late(result) }
              >);
              >
              >- Kiran Makam
              Thanks for something to test out.

              -Joe

              Comment

              • Kiran Makam

                #8
                Re: Dynamically generated functions with variable-based payload

                On Sep 25, 6:06 pm, Dahak <Dahak...@theXO UTfifthimperium .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
                getElementsByCl assName (google to find implementations of
                getElementsByCl assName function, Prototype has one)

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

                MyTranslationDi v.prototype.upd ate = function(result ){
                if (!result.error) {
                this.div.innerH TML = result.translat ion;
                }else{
                this.div.innerH TML = "Error happened";
                }

                }

                MyTranslationDi v.prototype.tra nslate = function(textTo Translate){
                var thisTmp = this;
                google.language .translate(
                textToTranslate , "en", "es",
                function(result ){thisTmp.updat e(result)}
                );

                }

                //get all elements having className as 'translate'
                var elements = getElementsByCl assName("transl ate");

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

                - Kiran Makam

                Comment

                • Jorge

                  #9
                  Re: Dynamically generated functions with variable-based payload

                  On Sep 25, 3:06 pm, Dahak <Dahak...@theXO UTfifthimperium .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 querySelectorAl l()
                  walkTheDOM(d.bo dy, function (node) {
                  if (node.className === 'translate') { toTranslate.pus h(node); }
                  });

                  //debug();
                  for (i= 0; i<toTranslate.l ength; ++i) {
                  e= toTranslate[i];
                  t= e.oldValue= e.firstChild.no deValue;
                  url= "xhrData.txt?"+ t+Math.random() ;
                  e.innerHTML= t+': XHR posted: '+url;
                  xhr= new XMLHttpRequest( );
                  xhr.open("GET", url, true);
                  xhr.onreadystat echange = (function (xhr, pDOMElement) {
                  return function () {
                  var txt= pDOMElement.old Value;
                  if (xhr.readyState == 4) {
                  txt+= ': '+xhr.responseT ext.substring(0 , 28);
                  } else {
                  txt+= ": readyState: "+xhr.readyStat e;
                  if (xhr.readyState == 3) { txt+= ": RECEIVING." }
                  }
                  pDOMElement.inn erHTML= txt;
                  };
                  })(xhr, e);
                  xhr.send(null);
                  }

                  function y (p, inner, br) {
                  var e;
                  if (br) { d.body.appendCh ild(d.createEle ment('br')) }
                  e= d.body.appendCh ild(d.createEle ment(p));
                  if (inner) { e.innerHTML= inner; }
                  return e;
                  };

                  function walkTheDOM (node, f){
                  f(node);
                  node= node.firstChild ;
                  while (node) {
                  walkTheDOM(node , f);
                  node= node.nextSiblin g;
                  }
                  };
                  };
                  </script>

                  Comment

                  • Dahak

                    #10
                    Re: Dynamically generated functions with variable-based payload

                    On Thu, 25 Sep 2008 07:25:37 -0700 (PDT), an orbiting mind-control
                    laser made Kiran Makam <kiranmn75@gmai l.comwrite:

                    -SNIP-
                    >To get all elements having a particular class, you can use
                    >getElementsByC lassName (google to find implementations of
                    >getElementsByC lassName function, Prototype has one)
                    Thanks. I'd already gotten that aspect running fine. The call to
                    getElementsByCl assName 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 MyTranslationDi v
                    >function MyTranslationDi v(objDiv){
                    this.div = objDiv;
                    >}
                    >
                    >MyTranslationD iv.prototype.up date = function(result ){
                    if (!result.error) {
                    this.div.innerH TML = result.translat ion;
                    }else{
                    this.div.innerH TML = "Error happened";
                    }
                    >
                    >}
                    >
                    >MyTranslationD iv.prototype.tr anslate = function(textTo Translate){
                    > var thisTmp = this;
                    > google.language .translate(
                    > textToTranslate , "en", "es",
                    > function(result ){thisTmp.updat e(result)}
                    > );
                    >
                    >}
                    >
                    >//get all elements having className as 'translate'
                    >var elements = getElementsByCl assName("transl ate");
                    >
                    >//iterate and translate
                    >for(var i=0, len=elements.le ngth; i<len; i++){
                    > var obj = new MyTranslationDi v( 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

                    Comment

                    Working...