关于javascript:将异步jQuery对话框更改为同步?

Change the asynchronous jQuery Dialog to be synchronous?

目前,我正在用jquery对话框替换"alert"/"confirm"。

但大多数遗留代码都是以异步方式编写的,这使得更改非常困难。有没有办法使jquery对话框以同步方式工作?(不使用循环或回调函数)

1
2
3
4
5
6
7
   For example:
   function run()
   {
      var result = confirm("yes or no");
      alert( result );
      \\more codes here
   }

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

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

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

1
2
3
4
5
6
    OK: function ()
   {
       $dialog.close();
       alert("yes");
       //more codes here
    }

这种方法是可行的,但是很难使所有的同步代码变成异步的,这需要大量的更改(参见下面的示例)。所以我在寻找同步jquery对话框,这是可能的吗??

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
     function f1()
    {
          if ( confirm("hello") )    f2();
          alert("no");
    }

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

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

另一个例子:

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

…这里,vendor对象触发一个事件,如果用户确认,则必须取消该事件。只有当事件处理程序同步返回false时,才能取消事件。


简短的回答是不,您将无法保持代码同步。这就是为什么:

  • 为了实现同步,当前正在执行的脚本必须等待用户提供输入,然后继续。
  • 当当前有一个正在执行的脚本时,用户无法与UI交互。事实上,在脚本执行完成之前,用户界面甚至不会更新。
  • 如果脚本在用户提供输入之前无法继续,而用户在脚本完成之前无法提供输入,则最接近的是挂起的浏览器。

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

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

请注意,您的页面背景还没有变红。在您继续执行并且脚本完成执行之前,它不会变为红色。当使用调试器暂停脚本执行时,也无法单击页面中的任何链接。

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

好消息是,转换代码并不难。大概,您的代码目前看起来是这样的:

1
2
3
4
5
6
7
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),您的代码看起来非常相同:

1
2
3
4
5
6
7
8
9
ifConfirm("continue?", function () {
    // several lines of code if yes
},
function () {
    // several lines of code if no
},
function () {
    // several lines of code finally
});

编辑:对于您添加到问题中的其他示例,很遗憾,代码需要重构。不可能有同步的自定义确认对话框。要在事件需要继续或取消的情况下使用自定义确认对话框,您只需始终取消该事件并在yesFn回调中模拟该事件。

例如,链接:

1
2
3
4
5
6
7
$("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;
    });
});

或者,一种形式:

1
2
3
4
5
6
7
$("form").submit(function (e) {
    e.preventDefault();
    var form = this;
    ifConfirm("Are you sure you're ready to submit this form?", function () {
        form.submit();
    });
});


我不确定不使用回调背后的动机是什么,所以很难判断什么解决方案可以满足您的需求,但是延迟执行的另一种方法是通过jquery的"延迟"对象。

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

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
    function confirm () {
        var defer = $.Deferred();
        $('Do you want to continue?').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/


不,您不能在javascript中执行任何同步操作(实际上,警报正在破坏规则)。javascript是以"单线程、异步"为核心构建的。

但是,您可以做的是禁用底层页面的功能(类似于lightbox),这样在不执行对话框操作(无论是确定还是取消)之前,不会从页面触发任何事件。认为这不能帮助您使同步代码正常工作。你必须重写。


这里有一些想法-你真正想要的是阻止你的异步事件,让它看起来像是同步的。以下是一些链接:

排队异步调用

莫布尔

叙述性JavaScript

希望这对你有进一步的帮助!!


要回答DavidWhiteman更具体的问题,下面是我如何实现LinkButton单击事件的"延迟"回发。基本上,我只是在阻止默认行为,并在用户反馈可用时手动触发回发。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
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');
            }
        }
        ]
    });
}

我不明白为什么您反对使用jquery回调来实现示例中的行为。您肯定需要重写一些代码,但类似于:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
    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" );
            }
        }
    });
}