0

On my website I have a javascript function that does an AJAX call to get account's information then opens a modal where you can see and edit the information. An AJAX call is used to change the detail in the database you select and then refreshes the original function to re-open the modal to refresh the information there. It seems however, that occasionally (1/2+ times done) the details don't update fast enough in the DB before the functions that collects the details to display is run again.

I want to try delaying when the second function is ran to give a better chance for the details being updated before being fetched again, however I am unsure on how to do this. I've tried various things, however the one that seems the most popular, as shown below, does not work. Do you have any suggestions how I can fix it so the code pauses for x time before continuing?

 function ChangeRank(StrUsername, StrPage)
 {
     var StrRank = $("#sltRank option:selected").val();
     //Updates info in database
     ModeratorEditAccount(StrUsername, StrRank, 'Rank', StrPage);
     //Displays info again
     setTimeout(ModeratorActions(StrUsername, StrPage), 30000);
 }

 function ModeratorEditAccount(StrUsername, Value, StrDetail, StrPage)
 {
     $.post('http://thomas-smyth.co.uk/functions/php/fncmoderatoreditaccount.php',
     {
         StrUsername: StrUsername,
         Value: Value,
         StrDetail: StrDetail
     }, function(data)
     {
         if (data == 0)
         {
             $("#mdlGeneral > div").modal("hide");
             if (StrSearchType == "Basic")
             {
                 UserBasicSearch(StrPage);
             }
             else
             {
                 UserAdvanceSearch(StrPage);
             }
         }
         else if (data == 10)
         {
             window.location.href = "http://thomas-smyth.co.uk/login.php";
         }
     });
 }

 function ModeratorActions(StrUsername, StrPage)
 {
     $.post('http://thomas-smyth.co.uk/functions/php/fncgetaccountdetails.php',
     {
         StrUsername: StrUsername
     }, function(data)
     {
         var returnValue = JSON.parse(data);
         if (data == 5)
         {}
         else
         {
             if (returnValue["StrGender"] == "M")
             {
                 StrGender = "Male";
             }
             else
             {
                 StrGender = "Female";
             }
             $("#mdlEditProfile").html('<div class="modal" tabindex="-1" role="dialog"><div class="modal-dialog" role="document"><div class="modal-content" style="border-radius: 25px;"><div class="box box-widget widget-user-2"><div class="widget-user-header bg-yellow"><div class="widget-user-image"><img class="img-circle" src="../dist/img/user2-160x160.jpg" alt="User Avatar"></div><h3 class="widget-user-username">' + returnValue['StrSurname'] + ', ' + returnValue['StrForename'] + ' (' + returnValue['StrUsername'] + ')</h3><h5 class="widget-user-desc">Member Since: ' + returnValue['DteRegDate'] + '</h5></div>\<div class="box-footer no-padding"><ul class="nav nav-stacked"><li><a><strong>Name: </strong>' + returnValue['StrSurname'] + ', ' + returnValue['StrForename'] + '<span class="pull-right badge bg-blue" style="cursor: pointer;" onclick="ChangeNameOpen(\'' + returnValue['StrUsername'] + '\', \'' + returnValue['StrSurname'] + '\', \'' + returnValue['StrForename'] + '\', \'' + StrPage + '\')"><i class="fa fa-fw fa-edit"></i> Edit</span></a></li><li><a><strong>Username: </strong>' + returnValue['StrUsername'] + '<span class="pull-right badge bg-blue" style="cursor: pointer;" onclick="ChangeUsernameOpen(\'' + returnValue['StrUsername'] + '\')"><i class="fa fa-fw fa-edit"></i> Edit</span></a></li><li><a><strong>Date of Birth: </strong>' + returnValue['DteDoB'] + '<span class="pull-right badge bg-blue" style="cursor: pointer;" onclick="ChangeDoBOpen(\'' + returnValue['StrUsername'] + '\', \'' + returnValue['DteDoB'] + '\')"><i class="fa fa-fw fa-edit"></i> Edit</span></a></li><li><a><strong>Gender: </strong>' + returnValue['StrGender'] + '<span class="pull-right badge bg-blue" style="cursor: pointer;" onclick="ChangeGenderOpen(\'' + returnValue['StrUsername'] + '\')"><i class="fa fa-fw fa-edit"></i> Edit</span></a></li><li><a><strong>Account Rank: </strong>' + returnValue['StrRank'] + '<span class="pull-right badge bg-blue" style="cursor: pointer;" onclick="ChangeRankOpen(\'' + returnValue['StrUsername'] + '\', \'' + returnValue['StrRank'] + '\', \'' + StrPage + '\')"><i class="fa fa-fw fa-edit"></i> Edit</span></a></li></ul></div></div></div></div></div>');
             $("#mdlEditProfile > div").modal("show");
         }
     });
 }   
jonrsharpe
  • 115,751
  • 26
  • 228
  • 437
Thomas Smyth
  • 512
  • 3
  • 9
  • 36

4 Answers4

3

setTimeout() may behave as expected when function is wrapped in function(){ ... }

setTimeout( function(){ ModeratorActions(StrUsername, StrPage) }, 30000);

See here: JavaScript.setTimeout

Community
  • 1
  • 1
Patrick Moore
  • 13,251
  • 5
  • 38
  • 63
  • That works great, thanks. It seems the articles I read were not very clear regarding that and used as an example `setTimeout( myFunction, 30000);` which I didn't realize required `function(){ ... }` when using my own function. Out of curiosity is there any reasoning why the syntax is like this? Is there any uses of putting something into the `function() {... }` parameters? – Thomas Smyth Dec 22 '16 at 22:45
2

You could use the promise that $.ajax returns, which will have the then property. You can then pass a function to the then property which will only be called when the Ajax call has finished. So then you don't need a timeout anymore:

function ModeratorEditAccount(StrUsername, Value, StrDetail, StrPage)
{
    // Return the promise to the caller 
    return $.post('http://thomas-smyth.co.uk/functions/php/fncmoderatoreditaccount.php',
       /// etc ...
}

Then in your ChangeRank function you can make use of the above change:

function ChangeRank(StrUsername, StrPage)
{
    var StrRank = $("#sltRank option:selected").val();
    //Updates info in database
    ModeratorEditAccount(StrUsername, StrRank, 'Rank', StrPage).then(function () { 
        // This only executes when previous ajax has returned
        //Displays info again
        ModeratorActions(StrUsername, StrPage);
    }, function () {
        // an error occurred, like a timeout.
        ModeratorActions(StrUsername, StrPage);
    });
}

Note that your timeout call was wrong anyway, since you did not pass a function reference to it, but executed ModeratorActions immediately. You could have used bind to pass the reference and still have the arguments passed to it when the actual call was made 30 seconds later:

setTimeout(ModeratorActions.bind(null, StrUsername, StrPage), 30000);

But this solution is still less practical compared to the promise-based solution, as the promise really fulfils when the Ajax transaction completes, and not a second later.

trincot
  • 317,000
  • 35
  • 244
  • 286
  • this seems to work well, however I have a few questions. What happens if for whatever reason no values are returned due to an error, etc.? Will it stay stuck waiting? Could I decrease the chance of this by using a try & catch in all my php functions to ensure a value is returned? – Thomas Smyth Dec 22 '16 at 23:01
  • 1
    It will not get stuck. The worst that can happen is a timeout, in which case the second argument to the `then` function can be used. I have added this to my answer now. Note that you can define a timeout period for ajax calls via the options argument. See the [jQuery documentation](http://api.jquery.com/jquery.ajax/). – trincot Dec 22 '16 at 23:07
1

Why not move the function to reload the data into the success method of the function used to save it? Rather than hoping the save request will complete in 3 seconds?

function ChangeRank(StrUsername, StrPage)
 {
     var StrRank = $("#sltRank option:selected").val();
     //Updates info in database
     ModeratorEditAccount(StrUsername, StrRank, 'Rank', StrPage);
     //Displays info again
     // Removed
     //setTimeout(ModeratorActions(StrUsername, StrPage), 30000);
 }

 function ModeratorEditAccount(StrUsername, Value, StrDetail, StrPage)
 {
     $.post('http://thomas-smyth.co.uk/functions/php/fncmoderatoreditaccount.php',
     {
         StrUsername: StrUsername,
         Value: Value,
         StrDetail: StrDetail
     }, function(data)
     {
         if (data == 0)
         {
             $("#mdlGeneral > div").modal("hide");
             if (StrSearchType == "Basic")
             {
                 UserBasicSearch(StrPage);
             }
             else
             {
                 UserAdvanceSearch(StrPage);
             }
             // Added here, though you may want to move it
             ModeratorActions(StrUsername, StrPage)

         }
         else if (data == 10)
         {
             window.location.href = "http://thomas-smyth.co.uk/login.php";
         }
     });
 }

 function ModeratorActions(StrUsername, StrPage)
 {
     $.post('http://thomas-smyth.co.uk/functions/php/fncgetaccountdetails.php',
     {
         StrUsername: StrUsername
     }, function(data)
     {
         var returnValue = JSON.parse(data);
         if (data == 5)
         {}
         else
         {
             if (returnValue["StrGender"] == "M")
             {
                 StrGender = "Male";
             }
             else
             {
                 StrGender = "Female";
             }
             $("#mdlEditProfile").html('<div class="modal" tabindex="-1" role="dialog"><div class="modal-dialog" role="document"><div class="modal-content" style="border-radius: 25px;"><div class="box box-widget widget-user-2"><div class="widget-user-header bg-yellow"><div class="widget-user-image"><img class="img-circle" src="../dist/img/user2-160x160.jpg" alt="User Avatar"></div><h3 class="widget-user-username">' + returnValue['StrSurname'] + ', ' + returnValue['StrForename'] + ' (' + returnValue['StrUsername'] + ')</h3><h5 class="widget-user-desc">Member Since: ' + returnValue['DteRegDate'] + '</h5></div>\<div class="box-footer no-padding"><ul class="nav nav-stacked"><li><a><strong>Name: </strong>' + returnValue['StrSurname'] + ', ' + returnValue['StrForename'] + '<span class="pull-right badge bg-blue" style="cursor: pointer;" onclick="ChangeNameOpen(\'' + returnValue['StrUsername'] + '\', \'' + returnValue['StrSurname'] + '\', \'' + returnValue['StrForename'] + '\', \'' + StrPage + '\')"><i class="fa fa-fw fa-edit"></i> Edit</span></a></li><li><a><strong>Username: </strong>' + returnValue['StrUsername'] + '<span class="pull-right badge bg-blue" style="cursor: pointer;" onclick="ChangeUsernameOpen(\'' + returnValue['StrUsername'] + '\')"><i class="fa fa-fw fa-edit"></i> Edit</span></a></li><li><a><strong>Date of Birth: </strong>' + returnValue['DteDoB'] + '<span class="pull-right badge bg-blue" style="cursor: pointer;" onclick="ChangeDoBOpen(\'' + returnValue['StrUsername'] + '\', \'' + returnValue['DteDoB'] + '\')"><i class="fa fa-fw fa-edit"></i> Edit</span></a></li><li><a><strong>Gender: </strong>' + returnValue['StrGender'] + '<span class="pull-right badge bg-blue" style="cursor: pointer;" onclick="ChangeGenderOpen(\'' + returnValue['StrUsername'] + '\')"><i class="fa fa-fw fa-edit"></i> Edit</span></a></li><li><a><strong>Account Rank: </strong>' + returnValue['StrRank'] + '<span class="pull-right badge bg-blue" style="cursor: pointer;" onclick="ChangeRankOpen(\'' + returnValue['StrUsername'] + '\', \'' + returnValue['StrRank'] + '\', \'' + StrPage + '\')"><i class="fa fa-fw fa-edit"></i> Edit</span></a></li></ul></div></div></div></div></div>');
             $("#mdlEditProfile > div").modal("show");
         }
     });
 }   
Tom
  • 4,257
  • 6
  • 33
  • 49
  • It would work in the scenario I listed, however this ModeratorEditActions function is used a lot to change account details and not just specifically in this situation, so stacking the functions like you have shown would make it less dynamic and not work in other situations. – Thomas Smyth Dec 22 '16 at 22:50
  • Ah, then the answer posted by @trincot would suit you better then. As, if for any reason your request takes longer than 3 seconds to return a result, you'll still suffer the same issue. – Tom Dec 22 '16 at 22:54
0

setTimeout(ModeratorActions(StrUsername, StrPage), 30000);

It's not the correct way to pass arguments to the callback, in your case ModeratorActions will be invoked immediately, so that probably explains why it works unreliably, you should use the following syntax:

setTimeout(ModeratorActions, 30000, StrUsername, StrPage)

Note: the above syntax passing additional arguments to callback function doesn't work for IE 9 and below, read setTimeout on Mozilla for workaround.

EDIT: I should type more fast next time.

Jerry Chin
  • 657
  • 1
  • 8
  • 25