Understanding Javascript callback parameters

2019-02-27 08:28发布

A few questions on callbacks, using some example code..

function uploadPhoto(params){

    var win = function(response){
       console.log("Success!");
       console.log("Response: ", response); 
    }

    var fail = function(error){
      console.log("Failed.");
      console.log("Error: ", error);
    }

    var options = {params: params};
    var ft = new FileTransfer();
    ft.upload(params.img_uri, "localhost:3000/testing"), win, fail, options);
}

In the above example, using FileTransfer we can upload an image to a server. If successful, it will call the win function, and if not successful, call the fail function.

How do the win and fail functions receive the response or error parameters? We're not calling win(response) or fail(error), so how does javascript accept these parameters without us setting them on our ft.upload() line?

More example code:

function win(response){
  console.log("Success!");
  console.log(response);
};

function fail(error){
  console.log("Failed.");
  console.log("Error: ", error);
};

function uploadPhoto(params){
  var options = {params: params};
  var ft = new FileTransfer();
  ft.upload(params.img_uri, "localhost:3000/testing"), win, fail, options);
};

In this second example, I've found that the win function can be called, however its 'response' parameter comes up undefined. What is the reason for this?

What if I wanted to have my win function to also accept params from the upload function, so it would look more like this:

var win = function(response, parameters){
  console.log("Success with parameters: ", parameters);
  console.log("Response: ", response);
}

Would my ft.upload line need to look more like this:

ft.upload(params.img_uri, "localhost:3000/testing", win(null, params), fail, options);

Lots of little questions here, Thanks for your help!

3条回答
混吃等死
2楼-- · 2019-02-27 08:37

How do the win and fail functions receive the response or error parameters? We're not calling win(response) or fail(error), so how does javascript accept these parameters without us setting them on our ft.upload() line?

ft.upload calls win or fail and supplies it with a value for response or error. That is, ft.upload might look something like this:

function upload(arg1, arg2, successCallback, failureCallback, options) {
  try {
    var response = something(...);
    successCallback(response);
  } catch(ex) {
    failureCallback(ex);
  }
}

Now, when you call ft.upload(params.img_uri, "localhost:3000/testing", win, fail, options), win is passed to upload as the successCallback argument and fail as failureCallback, and upload in turn calls one or the other with an appropriate argument.

What if I wanted to have my win function to also accept params from the upload function, so it would look more like this:

var win = function(response, parameters){
  console.log("Success with parameters: ", parameters);
  console.log("Response: ", response);
}

Would my ft.upload line need to look more like this:

ft.upload(params.img_uri, "localhost:3000/testing"), win(null, params), fail, options);

Close, but not quite. If you do that, you're passing whatever win and fail return as arguments, and unless they return functions that will be a problem when ft.upload tries to call them.

...which, not coincidentally, is one solution: Make them return functions that ft.upload can call:

function makeSuccessCallback(parameters) {
  return function successCallback(response) {
    console.log("Success with parameters: ", parameters);
    console.log("Response: ", response);
  }
}

var win = makeSuccessCallback(params);
ft.upload(params.img_uri, "localhost:3000/testing", win, fail, options);

The other option is to use Function.prototype.bind. You may have seen bind used to set a function's this object, but we can also pass additional arguments to bind and the returned function will use those arguments as its own, initial arguments. In computer science this is called "currying." For example:

function add(n, m) {
  return n + m;
}

var addThree = add.bind(null, 3);
addThree(4); // => 7

var ten = add.bind(null, 4, 6);
ten(); => 10

(Since this function doesn't care about this, we just pass null for the first argument.)

With callbacks we can use this to get a "copy" of a function but with its first argument(s) "built in":

function successCallback(parameters, response) {
  console.log("Success with parameters: ", parameters);
  console.log("Response: ", response);
}

var win = successCallback.bind(null, params);
ft.upload(params.img_uri, "localhost:3000/testing", win, fail, options);

This way, when ft.upload calls win(response), it will actually be calling successCallback(params, response).

查看更多
姐就是有狂的资本
3楼-- · 2019-02-27 08:37

First of all: there is a syntax error at ft.upload(params.img_uri, "localhost:3000/testing"), win, fail, options);. Remove the parenthesis after "localhost:3000/testing".

I don't know anything about the upload function you use, so I do not know if you pass the other parameters correctly. Let's assume all is done well.

Your first question was how the win and fail functions are called.

In JavaScript you can pass functions to another function. That's what you are doing here. You pass function that you define the the upload function. It's the responsibillity of the upload function to handle these function parameters.

I would assume that it creates a http object sends the data to the server as an octet stream and when the transfer will have completed it will call one of the function either win if the transfer was successfull or fail if the transfer failed. It depends on its implementation which parameter it will passes when it calls win or fail. In you case it might be the response of the http object or an error on failure.

Your second question was how you can pass your params to the callback.

In the first case you define the functions in a context where the params variable is defined (as parameter of the surrounding function). Functions in JavaScript are closures or lambda function which means, that they can use variables of the scope/context where they are defined. So in the first example you don't need to pass the params variable. You can use it directly in you functions. In the second example the function has no access to the params variable because the callback are defined outside of their scope. Then you have to create partial functions (which is to much to explaine it here, but if you are interested I'll add an example. Just drop a comment ;-)) or bind the params object to the callback. If you bind it to the callback you can access the params object inside the callback as this. There is also a special form of partial functions which is called function currying which would work here.

查看更多
爱情/是我丢掉的垃圾
4楼-- · 2019-02-27 08:56

How do the win and fail functions receive the response or error parameters?

It's not receiving this automatically, It's someone who wrote the code there which is calling win or fail from upload function in the following way:

if (success) {
    win(response)
} else if(failure) {
    fail(error)
}

If, in upload, you're not calling this then these functions won't get called. JavaScript do what you say it to do.

In this second example, I've found that the win function can be called, however its 'response' parameter comes up undefined. What is the reason for this?

win function can be called in the first example either. If you calling win like this: win(), then you will get undefined response; however if you send some data, like: win({foo: "foo"}), then you'll not get undefined, instead you'll get data that you've passed it.

What if I wanted to have my win function to also accept params from the upload function,

you've to pass params to upload, and then call win from there using:

win(response, params);

and win definition would be like:

function win(response, params) {...}

or

var win = function(response, params) {...}

Would my ft.upload line need to look more like this: ft.upload(params.img_uri, "localhost:3000/testing", win(null, params), fail, options);

Yes, you can do this but it'll pass upload then value returned by win. In your case, this is probably not required.

查看更多
登录 后发表回答