将异步jQuery对话框更改为同步?

时间:2011-08-05 20:33:57

标签: javascript jquery asynchronous dialog synchronous

目前,我正在努力用jquery对话框替换“alert”/“confirm”。

但是大多数遗留代码是以某种异步方式编写的,这使得它很难改变。有没有办法让jquery对话框以同步方式工作? (不要使用循环或回调函数)

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

在此示例中,警报和其他代码将在用户选择后执行。

如果我们使用jquery对话框 var result = $ dialog.open() 它将继续执行异步警报。

目前,我的解决方案是在OK | Cancel功能中使用回调函数。         例如:

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

此方法有效,但很难使所有同步代码变为异步,这需要进行大量更改(请参阅下面的示例)。 所以我正在寻找同步jQuery对话框,有可能吗?

例如:(实际代码比下面的例子复杂得多)

     function f1()
    {
          if ( confirm("hello") )    f2();
          alert("no");
    } 

    function f2()
    {
          if( confirm("world") )    f3();
          alert("no");
    }

    function f3()
    {
          return confirm("!") ;
    }

另一个例子:

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

...此处供应商对象触发一个事件,如果用户确认,该事件必须被取消。只有事件处理程序同步返回false时才能取消该事件。

7 个答案:

答案 0 :(得分:35)

简短的回答是否定的,您将无法保持代码同步。原因如下:

  • 为了使其同步,当前正在执行的脚本必须等待用户提供输入,然后继续。
  • 虽然当前正在执行脚本,但用户无法与UI进行交互。实际上,在脚本执行完之后,UI才会更新。
  • 如果脚本无法继续,直到用户提供输入,并且用户在脚本完成之前无法提供输入,那么您将获得的最接近的是挂起的浏览器。

为说明此行为,请调试代码并在更改UI的行后面的行上设置断点:

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

请注意,您的网页背景还不是红色。在您恢复执行并且脚本完成执行之前,它不会更改为红色。当您的调试器暂停执行脚本时,您也无法单击页面中的任何链接。

alert()confirm()此规则有例外。这些是浏览器控件,其处理方式与实际网页UI元素不同。

好消息是转换代码真的不是很难。据推测,您的代码目前看起来像这样:

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

你的异步版本可以创建一个函数ifConfirm(text, yesFn, noFn, finallyFn),你的代码看起来非常相似:

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

修改:为了回应您添加到问题中的其他示例,遗憾的是,该代码需要重构。只有不可能才能拥有同步自定义确认对话框。要在事件需要继续或取消的场景中使用自定义确认对话框,您只需要始终取消事件并模仿yesFn回调中的事件。

例如,链接:

$("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;
    });
});

或者,表格:

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

答案 1 :(得分:8)

我不确定不使用回调的动机是什么,因此很难判断哪种解决方案可能满足您的要求,但延迟执行的另一种方法是通过jQuery的“延迟”对象。

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

您可以设置一个打开jquery对话框的函数,并添加“等待”对话框关闭的代码。在你已经布局的情况下,这最终会以与回调相似的方式工作,但这是一个例子:

    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);

            });
        });

    });

这里有jsFiddle:http://jsfiddle.net/FJMuJ/

答案 2 :(得分:3)

不,你不能在Javascript中做任何同步(警报实际上违反了规则)。 Javascript是在核心中使用“单线程,异步”构建的。

但是,您可以执行的操作是禁用基础页面的功能(类似灯箱),因此在您不执行对话操作之前,不会从页面触发任何事件,无论是OK还是取消。认为这无助于您使同步代码正常工作。你必须改写。

答案 3 :(得分:1)

以下是一些想法 - 您真正想要的是阻止异步事件使其看起来像同步。这是一些链接:

Queuing async calls

Mobl

Narrative JavaScript

希望这可以帮助你进一步!!

答案 4 :(得分:0)

回答大卫怀特曼更具体的问题,这里是我如何实施一个延迟的&#34; LinkBut​​ton Click事件的回发。基本上,我只是防止默认行为并在用户反馈可用时手动触发回发。

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');
            }
        }
        ]
    });
}

答案 5 :(得分:0)

您可以使用真实的模态对话框。

[对话框]是网页中弹出框的元素,包括模式选项,该选项将使页面的其余部分在使用过程中保持惰性。在阻止用户交互直到用户给您答复或确认操作之前,这可能很有用。

https://github.com/GoogleChrome/dialog-polyfill

答案 6 :(得分:-1)

我真的不明白为什么你反对使用Jquery回调来实现你的例子中的行为。你肯定要重写一些代码,但是:

    function f1() { 
    $( "#testdiv" ).dialog({
        modal: true,
        buttons: {
            "OK": function() {
                $( this ).dialog( "close" );
                f2();
            },
            Cancel: function() {
                $( this ).dialog( "close" );
                alert('no');
            }
        }
    });
}
function f2() {
      $( "#testdiv2" ).dialog({
        modal: true,
        buttons: {
            "OK": function() {
                $( this ).dialog( "close" );
                f3();
            },
            Cancel: function() {
                $( this ).dialog( "close" );
                alert('no');
            }
        }
    });
}
function f3() {
    $( "#testdiv3" ).dialog({
        modal: true,
        buttons: {
            "OK": function() {
                $( this ).dialog( "close" );
            },
            Cancel: function() {
                $( this ).dialog( "close" );
            }
        }
    });
}

    <div id="testdiv" title="Hello"/>
    <div id="testdiv2" title="World"/>
    <div id="testdiv3" title="!"/>