web-dev-qa-db-fra.com

Modifier le dialogue jQuery asynchrone pour qu'il soit synchrone?

Actuellement, je travaille pour remplacer "alert"/"confirm" par la boîte de dialogue jquery.

Mais la plupart des codes existants sont écrits de manière asynchrone, ce qui rend difficile toute modification. Existe-t-il un moyen de faire en sorte que le dialogue jQuery fonctionne de manière synchrone? (n'utilisez pas de fonction loop ou callback)

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

Dans cet exemple, l'alerte et d'autres codes seront exécutés après le choix de l'utilisateur.

Si nous utilisons jquery dialog Var result = $ dialog.open () .__, il continuera d'exécuter l'alerte, qui est asynchrone.

Actuellement, ma solution consiste à utiliser la fonction de rappel dans la fonction OK | Annuler . Par exemple:

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

Cette méthode fonctionne mais il est difficile de faire en sorte que tous les codes synchrones deviennent asynchrones, ce qui nécessite de nombreuses modifications (voir l'exemple suivant). Alors je cherche le dialogue jQuery synchrone, est-ce possible ??

Par exemple: (Les vrais codes sont beaucoup plus compliqués que l'exemple suivant)

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

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

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

Un autre exemple:

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

... ici, l'objet vendeur déclenche un événement qui doit être annulé si l'utilisateur le confirme. L'événement ne peut être annulé que si le gestionnaire d'événements renvoie false - de manière synchrone.

36
user881284

La réponse courte est non, vous ne pourrez pas garder votre code synchrone. Voici pourquoi:

  • Pour que cela soit synchrone, le script en cours d'exécution devrait attendre que l'utilisateur fournisse une entrée, puis continuer.
  • Bien qu'un script soit en cours d'exécution, l'utilisateur ne peut pas interagir avec l'interface utilisateur. En fait, l'interface utilisateur ne se met même pas à jour avant que le script ne soit exécuté.
  • Si le script ne peut pas continuer jusqu'à ce que l'utilisateur fournisse une entrée, et que l'utilisateur ne puisse pas fournir une entrée tant que le script n'est pas terminé, le navigateur le plus proche que vous obtiendrez jamais est un navigateur bloqué. 

Pour illustrer ce problème, déboguez votre code et définissez un point d'arrêt sur la ligne suivant une ligne qui modifie l'interface utilisateur:

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

Notez que l’arrière-plan de votre page n’est pas encore rouge. Il ne passera pas au rouge jusqu'à ce que vous repreniez l'exécution et que le script se termine. Vous ne pouvez pas non plus cliquer sur les liens de votre page tant que l'exécution du script est suspendue avec votre débogueur.

Il existe une exception à cette règle pour alert() et confirm(). Ce sont des contrôles de navigateur et sont traités différemment des éléments d'interface utilisateur de page Web.

La bonne nouvelle est qu'il ne devrait vraiment pas être très difficile de convertir votre code. Votre code ressemble probablement à ceci:

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

Votre version asynchrone pourrait créer une fonction ifConfirm(text, yesFn, noFn, finallyFn) et votre code se ressemblerait beaucoup:

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

Edit: En réponse à l'exemple supplémentaire que vous avez ajouté à votre question, ce code devra malheureusement être refactoré. Il est simplement pas possible d’avoir des boîtes de dialogue de confirmation personnalisées synchrones. Pour utiliser une boîte de dialogue de confirmation personnalisée dans le cas où un événement doit continuer ou être annulé, il vous suffit de toujours l'annuler et de l'imiter dans le rappel yesFn.

Par exemple, un lien:

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

Ou un formulaire:

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

Je ne sais pas exactement quelle est la motivation qui pousse à ne pas utiliser les rappels. Il est donc difficile de déterminer quelle solution peut satisfaire vos besoins, mais un autre moyen de retarder l'exécution consiste à utiliser l'objet "différé" de jQuery. 

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

Vous pouvez configurer une fonction qui ouvre la boîte de dialogue jQuery et ajouter un code qui "attend" la fermeture de la boîte de dialogue. Cela finit par fonctionner de manière assez similaire à un rappel dans le cas que vous avez présenté, mais voici un exemple:

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

            });
        });

    });

Ça marche ici dans jsFiddle: http://jsfiddle.net/FJMuJ/

8
Ben L

Non, vous ne pouvez rien synchroniser en Javascript (alerte enfreint les règles, en fait). Javascript est construit avec "single threaded, async" dans le noyau.

Ce que vous pouvez faire, cependant, est de désactiver les fonctionnalités de la page sous-jacente (de type lightbox) afin qu'aucun événement ne soit déclenché depuis la page tant que vous n'avez pas effectué l'action de dialogue, que ce soit OK ou Annuler. Je pensais que cela ne vous aidait pas à faire fonctionner votre code de synchronisation. Vous devez réécrire.

3
user1046334

Voici quelques idées - ce que vous voulez réellement, c'est bloquer votre événement asynchrone pour qu'il ressemble à une synchronisation. Voici quelques liens:

Mise en file d'attente d'appels asynchrones

Mobl

JavaScript narratif

J'espère que cela vous aide plus loin !!

1
Leon

Pour répondre à la question plus spécifique de David Whiteman, voici comment j'implémente une publication "différée" pour un événement LinkButton Click. En gros, je ne fais que prévenir le comportement par défaut et déclencher la publication manuellement lorsque les commentaires des utilisateurs sont disponibles.

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