0

I am refactoring javascript and have a lot of similar POST calls to the same PHP url.

It would be nice to pass the postdata and callback function (defined in JiraChangeStatus), to a common function (SendPost).

I'm new to javascript, and was under the impression that it was possible to use a pointer / reference if the variable was the property of an object.

Have also tried using a variable for "xhr" instead of an object property, declaring "xhr" within JiraChangeStatus instead of SendPost, and even declaring it globally as a sanity check.

function JiraChangeStatus(index) { 

    var postdata = "&status="+document.getElementById("jiraT"+(index).toString()).value;

    SendPost("changestatus.php",postdata, function(obj) {
        alert("readyState: "+obj.xhr.readyState+"\r\nstatus: "+obj.xhr.status);                            
    });                                                  
}

function SendPost(module,postdata,fn)
{
    var obj = {xhr: new XMLHttpRequest()}
    var issuekey = GetJson(document.getElementById("issue").value,'key');                                              
    obj.xhr.open("POST", "modules/jira/"+module, true);
    obj.xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
    obj.xhr.onreadystatechange = fn(obj);
    obj.xhr.send("username=user&issuekey="+issuekey+postdata);  
}

When the callback function is executed I always see readystate 1 and status 0. I expect to see 4 and 200.

It appears javascript is passing a copy of the xhr object to the callback rather than the actual object.

These functions work when merged. Unless the properties of "xhr" are set within the scope of the callback function for "xhr", the callback doesn't get the value.

Please let me know what I'm doing wrong.

  • 2
    `obj.xhr.onreadystatechange = fn(obj)` immediately invokes `fn()`. It means "set the 'onreadystatechange' property to the result of calling the function `fn`". – Pointy Jun 21 '19 at 14:53
  • You don't need an object wrapper here. You can just pass the `xhr` object itself to the function. And in case of an `onreadystatechange` callback, you don't even need to pass anything - the function can access the xhr using `this` or `event.target`. – Bergi Jun 21 '19 at 14:55

1 Answers1

0

Thanks to Pointy and Bergi.

When it comes to solving my user story, there were 2 problems with the code.

The first was that when I used obj.xhr.onreadystatechange = fn(obj), it instantly evaluated the fn. That instant evaluation caused "this" to have a mouse click as the event trigger, rather than onreadystatechange.

The second was redundancy. There was no need to pass xhr as a parameter when "this" references xhr.

This code doesn't work (irrelevant lines omitted):

function JiraChangeStatus(index) { 
    SendPost("changestatus.php",postdata, function(pass) {
        console.log("This:\r\n"+this); //This: [object Window]                                 
    });                                                  
}
function SendPost(module,postdata,fn) {  
    var xhr = new XMLHttpRequest();     
    xhr.onreadystatechange = fn();       
}

This code works fine (irrelevant lines omitted):

function JiraChangeStatus(index) { 
    SendPost("changestatus.php",postdata, function(pass) {
        console.log("This:\r\n"+this); //This: [object XMLHttpRequest]                                 
    });                                                  
}
function SendPost(module,postdata,fn) {  
    var xhr = new XMLHttpRequest();     
    xhr.onreadystatechange = fn;   //no parentheses here fixed my use case    
}

Therefore I would accept Pointy and Bergi's comments as they solved my use case.

However, the question I posted was about passing a reference into a callback function and I want to give useful info to people who find it in a search.

The answer to my question of how to pass reference to a callback when parentheses cause immediate evaluation was here: How can I pass a parameter to a function without it running right away?

To validate that it "worked" for my use case I wrote some really ugly and unnecessary code which shows that you can pass a parameter into a callback function with parenthesis by simply having its immediate evaluation return a function.

Since JavaScript allows objects to be assigned by reference, and "xhr" is an object, as Bergi said I would not have needed an object wrapper (irrelevant lines omitted):

function SendPost(module,postdata,fn)
{         
    var xhr = new XMLHttpRequest();      
    xhr.onreadystatechange = fn(xhr); //immediately evaluates fn
}
function JiraChangeStatus(index) { 
    SendPost("changestatus.php",postdata, function(pass) {
        //the result of immediate evaluation is a function 
        //with the parameter "pass" in its scope
        return function() {
            console.log("This:\r\n"+this); //This: [object XMLHttpRequest]
            console.log(this.responseText); //returns the expected response text                
            console.log("Passed Reference:\r\n"+pass); //Passed Parameter: [object XMLHttpRequest]
            console.log(pass.responseText);
            //Both of these stop future callbacks
            //this.abort();
            //pass.abort();
        }
    });                                                  
}