web-dev-qa-db-ja.com

非同期jQueryダイアログを同期に変更しますか?

現在、「アラート」/「確認」をjqueryダイアログに置き換える作業を行っています。

ただし、ほとんどのレガシーコードは非同期方式で記述されているため、変更が困難です。jqueryダイアログを同期方式で動作させる方法はありますか?(ループまたはコールバック関数を使用しないでください)

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

この例では、ユーザーが選択した後にアラートおよびその他のコードが実行されます。

Jquery dialog 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を同期的に返す場合にのみキャンセルできます。

36
user881284

簡単な答えは「いいえ」です。コードの同期を維持することはできません。その理由は次のとおりです。

  • これを同期させるために、現在実行中のスクリプトはユーザーが入力を提供するのを待ってから続行する必要があります。
  • 現在実行中のスクリプトがありますが、ユーザーはUIを操作できません。実際、UIはスクリプトの実行が完了するまで更新されません。
  • ユーザーが入力を提供するまでスクリプトを続行できず、スクリプトが終了するまでユーザーが入力を提供できない場合、最も近いのはハングしたブラウザーです。

この動作を説明するには、コードをデバッグし、UIを変更する行に続く行にブレークポイントを設定します。

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

ページの背景がまだ赤ではないことに注意してください。実行を再開し、スクリプトの実行が完了するまで、赤に変わりません。デバッガでスクリプトの実行を一時停止している間は、ページ内のリンクをクリックすることもできません。

alert()およびconfirm()には、このルールには例外があります。これらはブラウザコントロールであり、実際のWebページ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();
    });
});
_
35
gilly3

コールバックを使用しないことの背後にある動機が何であるかは正確にはわからないため、どのソリューションが要件を満たす可能性があるかを判断することは困難ですが、実行を遅らせる別の方法は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/

8
Ben L

いいえ、Javascriptで同期を行うことはできません(実際、アラートは規則に違反しています)。 Javascriptは、コアに「シングルスレッド、非同期」で構築されています。

ただし、基になるページの機能を無効にすることで(ライトボックスのような)、ダイアログアクション(OKまたはキャンセル)を実行しない限り、ページからイベントがトリガーされません。これは、同期コードを機能させるのに役立ちません。書き換える必要があります。

3
user1046334

ここにいくつかのアイデアがあります-実際に必要なのは、非同期イベントをブロックして同期のように見せることです。ここにいくつかのリンクがあります:

非同期呼び出しのキューイング

Mobl

ナラティブJavaScript

これがあなたをさらに助けることを願っています!!

1
Leon

David Whitemanのより具体的な質問に答えるために、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');
            }
        }
        ]
    });
}
0
Sue Maurizio