2

I have a simple get function using XMLHttpRequest which accepts a callback parameter. The plan is to invoke the callback on the onload event handler.

Here is a simplified version:

get(url,doit);

function doit(data) {
    alert(data)
}
function post(url,callback) {
    var xhr=new XMLHttpRequest();
    xhr.onload=function() {                     //  Version 1
        callback(xhr.response)
    }
    xhr.onload=callback.bind(xhr,xhr.response); //  Version 2
    xhr.open('get',url,true);
    xhr.send(data);
}

The callback function has one parameter, data which is supposed to be the response from the Ajax Call.

I have 2 versions of invoking the callback:

  1. Version 1 simply calls the callback inside the event handler function.
  2. Version 2 uses bind to do the same; this is set to the xhr object, while the xhr.response is sent as the argument.

When I use Version 1, everything works as expected. When I use Version 2, the data parameter is empty.

I thought I knew XMLHttpRequest and .bind() reasonably well, but I cannot work out why the second version is empty. What is (not) happening here?

Comment

Thanks to the answers, I think I have it.

.bind() is executed immediately with the current value of xhr.responseText, which, at this stage, is nothing.

It appears that the first Version is preferable if I need to respond with a future value.

Thanks all.

Manngo
  • 14,066
  • 10
  • 88
  • 110
  • 1
    When you execute `.bind(xhr,xhr.response)` there isn't any response yet -> `xhr.response === undefined` – Andreas Jun 24 '17 at 09:06

2 Answers2

1

It's because callback.bind will be executed immediately. And at the time it is executed, xhr.responseText is obviously not available since the request has not been completed yet. You can try this to see the result.

function doit(data) {
    alert(data, this.responseText);
}
Lewis
  • 14,132
  • 12
  • 66
  • 87
0

This is the behavior of bind: when parameter is binded, the binded value is the referenced object, or the primitive value at that moment.

Here is a simple code snippet:

function operate(callback) {
  let a = {
    display: 'AAA'
  };
  setTimeout(callback.bind('ThisObj', a.display), 1000);
  a.display = 'AAA222';
}

operate(function(data) {
  console.log(data);
});

The printed result is: AAA, as a.display is binded and a.display is a primitive type (String) -- the value at the bind moment (AAA) is passed.

This is just what happened for xhr.response, as its type is String.


However, if an object is bind as parameter, that object's reference will be passed, which means the binded function would get the latest value:

function operate(callback) {
  let tmp = {
    t: 'AAA'
  };
  let a = {
    display: tmp
  };
  setTimeout(callback.bind('ThisObj', a.display), 1000);
  tmp.t = 'AAA222';
}

operate(function(data) {
  console.log(data);
});

The printed result is: { t: 'AAA222' }

Please note: if variable a in the above example changes its value to another object, the binded reference value does not change (still the previous referenced object):

function operate(callback) {
  let a = {
    display: 'AAA'
  };
  setTimeout(callback.bind('ThisObj', a), 1000);
  a = {
    display: 'AAA222'
  };
}

operate(function(data) {
  console.log(data);
});

The printed result is: { display: 'AAA' }

shaochuancs
  • 15,342
  • 3
  • 54
  • 62