-1

The following code snippet explains a situation:-

function submitHandler(form) {
    return x(form) && y(form);
}

x is an external function which validates the form. It returns false if form is not validated and if validated it submits the form.

y is a function, which should be called only if form is validated. It makes an AJAX call, JSONP type. JSONP ajax calls can only be async.

Y internally submits the form after getting the response. y will always return false.

I'm not sure what x does exactly but I can confirm y is called but AJAX call inside it is cancelled.

When I create a break-point and run in firebug debug mode, the AJAX call is successful. Thus, I think creating a delay can solve the problem.

Is there any way to create a delay after the AJAX call?

Update:

The function y:-

function y(jsonStr) {
    jQuery.ajax({
        url: url,
        contentType: 'application/json',
        dataType: 'jsonp',
        data: {
            jsonInfo: jsonStr,
            jsonpCallback: 'submitForm'
        }
    });
}

update 2:

The function x (edited)

function x(form) {
    clearError();

    var submitButton = null;
    var allowSubmit = true;

    // Some code

    for ( var i = 0; i < form.elements.length; i++) {
        var fld = form.elements[i];
        if (!validateField(fld)) {
            allowSubmit = false;
        }
    }

    if (allowSubmit) {
        if (!pageSubmitted) {
            pageSubmitted = true;

            // some code

            form.submit();

            if (submitButton != null) {

                submitButton.disabled = true;
                submitButton.value = getPleaseWaitMessage();
            }
        } else {
            allowSubmit = false;
        }
    }

    // Some code

    return allowSubmit;
}
Kaushik
  • 3,371
  • 3
  • 23
  • 32

2 Answers2

3

Introducing a delay like you suggested is very unreliable (e.g. what happens when the time it takes to receive the response from the AJAX call is longer than your delay?). You should, instead take a callback-based approach. For example:

The markup:

<div id="progress" style="display: none">Just a moment...</div>

<form onsubmit="return submitHandler(this);">
  <!-- ... -->
</form>

Here's a little helper type which will help maintain the link between the JSONP calls and specific client callbacks (e.g. the one passed as a second parameter to y - demonstrated further down):

var JSONPCallbackDispatcher = {
  _callbacks: [],

  register: function(callback) {
    var token = new Date().getTime();

    this._callbacks[token] = callback;
    return token;
  },

  dispatch: function(token, args) {
    if (this._callbacks[token]) {
      this._callbacks[token](args);
      delete this._callbacks[token];
    }
  }
}

function submitForm(token, response) {
  JSONPCallbackDispatcher.dispatch(token, response);
}

Now, the main logic:

function submitHandler(form) {
  var ret = x(form); // This is synchronous

  if (ret) {
    progress(form, true);

    y($(form).serialize(), function (result) { 
      progress(form, false);

      if (!result.error) {
        form.submit();
      }
    });
  }

  return ret;
}

function y(payload, callback) {
  var url = '...', token = JSONPCallbackDispatcher.register(callback);

  $.ajax({
    url: url,
    contentType: 'application/json',
    dataType: 'jsonp',
    data: {
        jsonInfo: payload,
        jsonpToken: token,
        jsonpCallback: 'submitForm'
    }
  });
}

// Shows/hides the progress indicator, toggles form controls' "disabled" state.
function progress(form, val) {
  var inputs = $(form).find('input, button, select');

  $('#progress').toggle(val);

  if (val) {
    inputs.attr('disabled', 'disabled');
  } else {
    inputs.removeAttr('disabled');
  }
}

Now the server can return the call to submitForm in the following form:

<script>
    submitForm([token received from "jsonpToken"], 
      { error: [was there an error?] });
</script>

The nice thing about this approach is that your call-specific callbacks are not spread across your client-side logic and from the caller perspective this looks a bit more like a promise (so you can easily swap the JSONP approach with complete/success callbacks when possible without affecting the way the y is called).

Hope this helps.

volpav
  • 5,090
  • 19
  • 27
  • The JSONP is required, as it is an cross site AJAX call, and I think `complete` etc wont work with JSONP calls. – Kaushik Dec 19 '13 at 08:53
  • There is another problem. As I mentioned x also submits the form after validating. So in this case the callbacks will never get executed. I've already used a callback `jsonpCallback: 'submitForm'` so the problem is not that. In your code `var ret = x(form);` will also not wait to get the response. Since, x is an external script I can't change it. That is why I was looking for a delay. – Kaushik Dec 19 '13 at 09:48
  • @Kaushik is `x` also makes an asynchronous call? If not, is it possible to swap the call to `x` and `y` so the `y` gets called first and then, if the validation succeeds, the call to `x` is made? – volpav Dec 19 '13 at 09:51
  • `x` is not asynchronous but it handles the form validation. I need to call `y` only after form validation. As far as I understand `x` submits the form internally, but when I debug the script in Firebug, `y` is executed after `x`, and if I wait for sometime before moving to the return statement after the AJAX call is made. But in real time doesn't work. Can we somehow just delay `form submit`? – Kaushik Dec 19 '13 at 10:07
  • @Kaushik I'm not sure I understand how you get to `y`, even when you're in a debugger, if the `x` is synchronous and is supposed to submit the form on its own (you wouldn't get to `y` in this case - the document would have been unloaded before that). Can we see what's inside `x` (maybe just in a meta language in case you can't paste the actual code)? – volpav Dec 19 '13 at 10:43
  • @Kaushik `setTimeout` won't work because the code inside will be executed somewhere else down the stack and returning from it is not the same as returning from `submitHandler` (in other words, that moment is *lost*). – volpav Dec 19 '13 at 10:46
  • let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/43508/discussion-between-kaushik-and-volpav) – Kaushik Dec 19 '13 at 11:53
  • 1
    @Kaushik hey, sorry - didn't notice that chat invite. Anyway, by looking at the body of `x`, I can only suggest you to either try and get a permission to alter it (so you can cancel the form submission) or "hack" it (e.g. by setting the `pageSubmitted` flag to `true` before calling the method) but I'd personally avoid the latter approach at all costs. – volpav Dec 19 '13 at 14:48
0

As per your requirement, you can add delay in execution of y(form) function as follow:

function y(jsonStr) {
            jQuery.ajax({
                url: url,
                contentType: 'application/json',
                dataType: 'jsonp',
                data: {
                    jsonInfo: jsonStr,
                    jsonpCallback: 'submitForm'
                },
                success: function (dataCheck) {
                    setTimeout(function () {
                        // Do something after 1 seconds delay
                        return true;
                    }, 1000);
                }
            });
        }
Bhavesh Kachhadiya
  • 3,902
  • 3
  • 15
  • 20
  • That won't work according to the way the `y` is called in the first place. Returning from the `success` callback is not the same as returning from the `y`! – volpav Dec 19 '13 at 08:23
  • Nevermind my previous comment, maybe that's exactly what is needed (since, as per question details, the `y` is supposed to submit the form on its own). But it's kind of not clear for me now, why the author would need a delay if he can just submit the form from within either the `success` callback or `submitForm` JSONP callback - the cancellation has probably nothing to do with any delays). – volpav Dec 19 '13 at 08:33