Change the asynchronous jQuery Dialog to be synchronous?

46,134

Solution 1

The short answer is no, you won't be able to keep your code synchronous. Here's why:

  • In order for this to be synchronous, the currently executing script would have to wait for the user to provide input, and then continue.
  • While there is a currently executing script, the user is unable to interact with the UI. In fact, the UI doesn't even update until after the script is done executing.
  • If the script can't continue until the user provides input, and the user can't provide input until the script is finished, the closest you'll ever get is a hung browser.

To illustrate this behavior, debug your code and set a break point on the line following a line that changes the UI:

$("body").css("backgroundColor", "red");
var x = 1;  // break on this line

Notice that your page background is not yet red. It won't change to red until you resume execution and the script finishes executing. You are also unable to click any links in your page while you've got script execution paused with your debugger.

There is an exception to this rule for alert() and confirm(). These are browser controls, and are treated differently than actual web page UI elements.

The good news is that it really shouldn't be very hard to convert your code. Presumably, your code currently looks something like this:

if (confirm("continue?")) {
    // several lines of code if yes
}
else {
    // several lines of code if no
}
// several lines of code finally 

Your asynchronous version could create a function ifConfirm(text, yesFn, noFn, finallyFn) and your code would look very much the same:

ifConfirm("continue?", function () {
    // several lines of code if yes
},
function () {
    // several lines of code if no
},
function () {
    // several lines of code finally 
});

Edit: In response to the additional example you added to your question, unfortunately that code will need to be refactored. It is simply not possible to have synchronous custom confirmation dialogs. To use a custom confirmation dialog in the scenario where an event needs to either continue or cancel, you'll just have to always cancel the event and mimic the event in the yesFn callback.

For example, a link:

$("a[href]").click(function (e) {
    e.preventDefault();
    var link = this.href;
    ifConfirm("Are you sure you want to leave this awesome page?", function () {
        location.href = link;
    });
});

Or, a form:

$("form").submit(function (e) {
    e.preventDefault();
    var form = this;
    ifConfirm("Are you sure you're ready to submit this form?", function () {
        form.submit();
    });
});

Solution 2

I'm not exactly sure what the motivation behind not using callbacks is so it is hard to judge what solution might satisfy your requirements, but another way to delay execution is through jQuery's "deferred" object.

http://api.jquery.com/category/deferred-object/

You could set up a function that opens the jquery dialog and add code that "waits" for dialog to close. This ends up working in a fairly similar way to a callback in the case you've laid out but here is an example:

    function confirm () {
        var defer = $.Deferred(); 
        $('<div>Do you want to continue?</div>').dialog({
                autoOpen: true,
                close: function () { 
                    $(this).dialog('destroy');
                },
                position: ['left', 'top'],
                title: 'Continue?',
                buttons: {
                    "Yes": function() {
                        defer.resolve("yes"); //on Yes click, end deferred state successfully with yes value
                        $( this ).dialog( "close" );
                    },
                    "No": function() {
                        defer.resolve("no"); //on No click end deferred successfully with no value
                        $( this ).dialog( "close" );
                    }
                }
            });
        return defer.promise(); //important to return the deferred promise
    }

    $(document).ready(function () {
        $('#prod_btn').click(function () {
            confirm().then(function (answer) {//then will run if Yes or No is clicked
                alert('run all my code on '  + answer);

            });
        });

    });

Here it is working in jsFiddle: http://jsfiddle.net/FJMuJ/

Solution 3

Here's some ideas - what you actually want is to block your async event to make it look like sync. Here's some links:

Queuing async calls

Mobl

Narrative JavaScript

Hope this helps you further!!

Solution 4

To answer David Whiteman's more specific question, here's how I'm implementing a "deferred" postback for a LinkButton Click event. Basically, I'm just preventing the default behaviour and firing the postback manually when user feedback is available.

function MyClientClickHandler(event, confirmationMessage, yesText, noText) {
    // My LinkButtons are created dynamically, so I compute the caller's ID
    var elementName = event.srcElement.id.replace(/_/g, '$');
    // I don't want the event to be fired immediately because I want to add a parameter based on user's input
    event.preventDefault();
    $('<p>' + confirmationMessage + '</p>').dialog({
        buttons: [
        {
            text: yesText,
            click: function () {
                $(this).dialog("close");
                // Now I'm ready to fire the postback
                __doPostBack(elementName, 'Y');
            }
        },
        {
            text: noText,
            click: function () {
                $(this).dialog("close");
                // In my case, I need a postback when the user presses "no" as well
                __doPostBack(elementName, 'N');
            }
        }
        ]
    });
}
Share:
46,134
Admin
Author by

Admin

Updated on May 16, 2020

Comments

  • Admin
    Admin almost 4 years

    Currently, I'm working to replace "alert'/"confirm" with the jquery dialog.

    But most of legacy codes is written in some asynchronous way, which make it difficult to change. Is there any way to make jquery dialog work in a synchronous way? ( don't use loop or callback function )

       For example:
       function run()
       { 
          var result = confirm("yes or no");
          alert( result );
          \\more codes here
       }
    

    In this example the alert and other codes will be executed after user's choice.

    If we use jquery dialog var result = $dialog.open() It will continue to execute the alert, which is asynchronous.

    Currently, my solution is to use call back function in the OK|Cancel function. For example:

        OK: function ()
       {
           $dialog.close();
           alert("yes");
           //more codes here
        }
    

    This method works but it is difficult to make all the synchronous codes become asynchronous, which requires a lot of change (see the following example). So I'm looking for the synchronous jQuery Dialog, is it possible??

    For example: ( The real codes are much more complicated than the following example)

         function f1()
        {
              if ( confirm("hello") )    f2();
              alert("no");
        } 
    
        function f2()
        {
              if( confirm("world") )    f3();
              alert("no");
        }
    
        function f3()
        {
              return confirm("!") ;
        }
    

    Another example:

    vendorObject.on('some-event', function() {
        if(confirm("Do you really want to do that?")) {
            return true;
        }
        else {
            return false; // cancel the event
        }
    });
    

    ... here the vendor object fires an event, which has to be cancelled if the user confirms. The event can only be cancelled if the event handler returns false - synchronously.

  • Richard JP Le Guen
    Richard JP Le Guen over 12 years
    The problem lies in the fact that an existing components and interfaces which cannot be changed depend on the synchronous nature of the calls. For example, I have to return false in an event handler depending on whether the user chose yes or no or another button.
  • Ben L
    Ben L over 12 years
    Can you give a more concrete example in your question of how these dependencies prohibit use of callbacks or other solutions mentioned? It isn't totally clear to me how answers such as @gilly3 or I have given do not meet your needs. You are going to have to use a structure that calls a function on the event of a user clicking a confirm button. I don't think there is a way around that in javascript. It is just a matter of how it is structured.
  • Richard JP Le Guen
    Richard JP Le Guen over 12 years
    No, you probably can't get around this in JavaScript. But I've faced this problem before, and I think it's an interesting one; wanted to see what solutions the community would bring forward. I'll add an example of the problems I faced to the question.
  • Richard JP Le Guen
    Richard JP Le Guen over 12 years
    Nice! Exactly what I did in the end, too :D
  • Leandro De Mello Fagundes
    Leandro De Mello Fagundes over 9 years
    @gilly3 awesome answer! congrats!