简体   繁体   中英

Call a function after an asynchronous JavaScript function completes?

I'm new to JavaScript, and I searched around for a solution to my problem. I'm really struggling to find the right answer. I think I might have to do something with callbacks, but I'm not sure.

Basically, I am implementing a "like" button on my website. On clicking the "like" button, I need two main back-end things to happen: The database of "likes" should be updated, and an email notification should be sent to the person who got a "like". After clicking the "like" button, I render a "liked" button to show the user that it went through.

Problem is, shooting the email takes a long time. So if I update the database and then send the email and then render the button, the user will be waiting a bit before he/she sees the button changed to a "liked" button. So instead, I would like to update the database, render the like button, and then shoot the email.

In my JavaScript, I tried doing the following:

function foo()
{
  xmlhttp1.onreadystatechange = liked( function() {
    xmlhttp2.open("POST", "async/async_likeEmail.php",true)
    xmlhttp2.setRequestHeader(...
    ...
    xmlhttp2.send(parameters);
  }

  xmlhttp1.open("POST", "async/async_likeUpdateDB.php", true)
  xmlhttp1.setRequestHeader(...
  ...
  xmlhttp1.send(parameters);
}

function liked(callback) {
  if (xmlhttp1.readyState == 4)
  {
    xmlhttp1.responseText;
    someElement.innerHTML = xmlhttp1.responseText; // render the "liked" button
    callback();
  }
}

Doing it this way, the asynchronous "liked" function fails to get called. For some reason, passing in a callback breaks it. I've also tried not passing in a callback and instead, defining a separate "like_notify" function, and calling like_notify() inside of the asynchronous "liked" function also fails.

Can someone let me know what I'm doing wrong and why these attempts have failed? Are callbacks not the solution to this problem?

Instead of waiting for the response from the server to get permission to update the user interface, a popular technique is to simply assume that it's okay to just go ahead and update the UI. In most cases, the request is usually successful, so just make that assumption and then make the requests to the server.

Afterwards, when the response comes back, instead of updating the UI, check to see if the request was indeed successful. If it was, great! You can either do nothing, since you've already updated your UI, or you could check to make sure your fast update matches what the server sent. It should, but if it doesn't, you can fix it.

Additionally, you can also check the status code in the response. If it's a 500, 404, or any non-200 status code that indicates that there is an exceptional case, you can then act on it by reverting your like button back to it's previous state and then displaying a nice error message to your user.

Not only does this make your UI extremely snappy, but you're still able to handle error conditions when they do occur.

Here is your code, modified slightly to facilitate this technique. Since I don't have all your code, this is not to be construed as a working example and is intended to simply get you started:

function foo()
{ 
   // call liked immediately. Don't wait for the server to respond
      // think "update the UI immediately"
   liked( function() {
       xmlhttp2.open("POST", "async/async_likeEmail.php",true)
       xmlhttp2.setRequestHeader(...
       ...
       xmlhttp2.send(parameters);
   });

  // this is a handler where you just check to make sure the request succeeded
    // if it does, great, do nothing. If it fails, change the like button back!
  xmlhttp1.onreadystatechange = function() {
      if(xmlhttp1.status == 404 || xmlhttp1.status == 503) {
          alert("problem!");
          // put the like button back to the previous state
      }
  };

  xmlhttp1.open("POST", "async/async_likeUpdateDB.php", true)
  xmlhttp1.setRequestHeader(...
  ...
  xmlhttp1.send(parameters);
}

function liked(callback) {

  /* insert code to render the liked result
   *  show what the button would look like if successful.
   */

  // you don't really need to do this part. You know what a liked button looks like
   // so just render it without waiting for the response.
    // however, leaving this here acts as a failsafe in case your  
     // code that updates it immediately does something wrong
  if (xmlhttp1.readyState == 4)
  {
    xmlhttp1.responseText;
    someElement.innerHTML = xmlhttp1.responseText; // render the "liked" button

    // call the callback to make the 2nd Ajax request
    callback();
  }
}

UPDATE:

The reason your first AJAX call was not firing properly is because you were assigning the result of thh function "liked" to the onreadystatechange handler instead of assigning the actual function object to the onreadystatechange handler:

xmlhttp1.onreadystatechange = liked( function() {
    xmlhttp2.open("POST", "async/async_likeEmail.php",true)
    xmlhttp2.setRequestHeader(...
    ...
    xmlhttp2.send(parameters);
}

Instead, this would have worked:

xmlhttp1.onreadystatechange = function() { 
    liked( function() {
        xmlhttp2.open("POST", "async/async_likeEmail.php",true)
        xmlhttp2.setRequestHeader(...
        ...
        xmlhttp2.send(parameters);
    }
}

To make it more clear, consider the following:

xmlhttp1.onreadystatechange = someFunct();   // assign the result to the handler

xmlhttp1.onreadystatechange = someFunct;    // assign the function to the handler

// also assigns function to handler, but wrapped in anonymous function
xmlhttp1.onreadystatechange = function() {   
    someFunct();
}
//abstract the ajax 
function ajaxpost(url,callback){

    //all the xhr setup stuff

    xmlhttp.open("POST", url, true)
    xmlhttp.onreadystatechange = function(){
        if (xmlhttp1.readyState == 4){

            //execute callback when done
            callback(xmlhttp.responseText);
        }
    }
    xmlhttp.send();
}

function like(){
    //update db
    ajaxpost("async/async_likeUpdateDB.php",function(data){
        //render like button
        someElement.innerHTML = xmlhttp1.responseText
        //email
        email();
    }
}

function email(){
    //hit email
    ajaxpost("async/async_likeEmail.php",function(data){
        //everything done
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM