3

I know that similar questions have been posted many times, however I've read many of them and can't find an answer to my problem.

I have a function that waits for an ajax request response. Many of you will ask why? Well, I'm using a Wizard Jquery Plugin which executes a function onLeaveAStepFunction when a step is left, then the wizard goes to the selected step if the return value from onLeaveAStepFunction is true; else it remains in the same step.

I'm doing this async: false for waiting and it works, but this is a bad design. Also, I can't use a blockUI plugin.

How can I do this?

Some code:

Initializing the wizard:

$("#wizard").smartWizard({
        onLeaveStep : onLeaveStepFunction,
    });

Calling the ajax request:

function onLeaveStepCallback(obj, context) {
    nextStep = sendForm();
}

The ajax request:

var nextStep = false;
$.ajax({
    url : path,
    type : "POST",
    async : false,
    data : $("#" + idForm).serialize(),
    success : function(data) {
        $("#" + idDiv).html(data);
        nextStep = !$("#" + idHiddenErrores).val())
    }
});

Omitting the attributes. Please help me.

J0e3gan
  • 8,740
  • 10
  • 53
  • 80
imarban
  • 1,017
  • 1
  • 9
  • 24
  • 1
    you can't do that because of the asynchronous nature of ajax.... – Arun P Johny Sep 10 '14 at 02:05
  • @Arun Then how can I block the un waiting for the ajax response? – imarban Sep 10 '14 at 02:17
  • @Arun Although AJAX is asynchronous in nature, it's simply not true you can't execute blocking AJAX requests when needed – Brian Bolli Sep 10 '14 at 02:19
  • @BrianBolli then it is not asynchronous in nature isn't it... if you are using the async request it is not possible but there are ways to make it synchronous but with costs... – Arun P Johny Sep 10 '14 at 02:20
  • Agreed, but once in a blue moon I've needed to execute a blocked AJAX request before executing alert. My assertion was only that it could be done, must have misread your comment. :) – Brian Bolli Sep 10 '14 at 02:26

3 Answers3

0

You could use the jQuery wait method. I took an example from docs page to highlight how you'd do it:

$.when( $.ajax( "/request.php" ) ).done(function( response ) {
        // response argument resolved from ajax requests
        // process any work after ajax call finishes
}

A link to docs page:

http://api.jquery.com/jquery.when/

Brian Bolli
  • 1,873
  • 1
  • 12
  • 14
  • The smart wizard API doesnt seem to support Deferred's though, its expecting an immediate return value not a promise so if he wants it to execute in the same spot as that sequence then hes kind of out of luck. – prodigitalson Sep 10 '14 at 02:20
  • Exactly, the wizard plugin requires a true or false immediate – imarban Sep 10 '14 at 02:26
  • 2
    Why do people wrap an ajax call in `$.when()`. There is no need to do that. You can just do `$.ajax().done()` directly. No benefit at all to using `$.when()` there with only one ajax call to wait on. – jfriend00 Sep 10 '14 at 03:23
  • It's certainly something which shouldn't be applied without a functional purpose, but I've used it before. For example, I needed a means to update website cache for specific product view via the administrator's interface. That involved retrieving the data and rendering a particular view six times for each language, caching each iteration. The benefit is the control to guarantee a block of code finishes executing before another. – Brian Bolli Sep 10 '14 at 03:54
  • 1
    @prodigitalson: Then one should consider using a *smarter* wizard plugin that does… :-) – Bergi Sep 11 '14 at 00:03
  • 1
    @Bergi totally agree... in fact if you don't need a lot of crazy features or a heavy level of abstraction itd probably be asier to write it from scratch or if you need the features, fork that one used and modify the callbacks to take promises - then issue a pull request. No reason why that thing shouldn't rely on events and/or promises instead of passing in a hard callback there. I'd call that bad design. – prodigitalson Sep 11 '14 at 01:58
0

I'm doing this async: false for waiting and it works, but this is a bad design also I can't use a blockUI plugin.

Unless your wizard is better designed and supports async callbacks (e.g., promise-returning ones), async:false is your only choice.

Consider switching to a different wizard, and don't forget to file a bug for the plugin that you're currently using.

Bergi
  • 630,263
  • 148
  • 957
  • 1,375
  • Unfortunately to add a block ui plugin was a late requirement. It's too expensive to change the wizard but thanks. – imarban Sep 11 '14 at 00:08
  • If changing to a completely different wizard is not an option, consider forking and patching the current plugin. Adding promise support should be a breeze. – Bergi Sep 11 '14 at 00:11
  • The block ui appears after the ajax request is done. I use it before the ajax request but it doesn't work. I also tried using it in beforeSend function jquery – imarban Sep 11 '14 at 00:13
  • You might be able to apply some tricks to [force a reflow](https://stackoverflow.com/search?q=force+reflow) after the DOM update which might lead to a repaint. If that doesn't work (or doesn't work in all target browsers), you're simply out of luck - change the plugin. – Bergi Sep 11 '14 at 00:24
0

One hackish work-around is to do it before leaveStep. Perhaps on showStep:

var wizard_next_step;
$("#wizard").smartWizard({
    onShowStep : function (obj, context) {
        onLeaveStepFunction(obj, context, function(nextStep){
            wizard_next_step = nextStep;
        });
    },
    onLeaveStep : function () {
        return wizard_next_step;
    }
});

You'd also need to modify your onLeaveStepFunction to accept a callback:

function onLeaveStepCallback(obj, context, callback) {
    nextStep = sendForm(callback);
}

And your ajax function should then be:

$.ajax({
    url : path,
    type : "POST",
    async : false,
    data : $("#" + idForm).serialize(),
    success : function(data) {
        $("#" + idDiv).html(data);
        callback( !$("#" + idHiddenErrores).val()) );
    }
});

Now, it looks like you're drawing into the wizard window with this:

$("#" + idDiv).html(data);

I'm entirely sure if this is the case. But if it is then you cannot do this here (obviously because it's onShowStep which would overwrite current content). If this is so you should pass the data in the callback:

success : function(data) {
    callback( data , !$("#" + idHiddenErrores).val()) );
}

Write the wizard like this:

var wizard_next_step;
var wizard_data;
$("#wizard").smartWizard({
    onShowStep : function (obj, context) {
        onLeaveStepFunction(obj, context, function(data, nextStep){
            wizard_data = data;
            wizard_next_step = nextStep;
        });
    },
    onLeaveStep : function (obj, context) {
        $("#" + idDiv).html(wizard_data);
        return wizard_next_step;
    }
});

The key is to call all the asynchronous functions and get the data long before you call all your synchronous functions.

Note: I don't know smart-wizard at all and not a serious jQuery user. The answer above is based on my 2 minutes reading smart-wizard documentation on github and my understanding of javascript. You will definitely need to modify my examples to make it work.

slebetman
  • 109,858
  • 19
  • 140
  • 171