web-dev-qa-db-fra.com

Attendez qu'une fonction avec animations soit terminée avant d'exécuter une autre fonction

J'ai un problème avec les fonctions normales ( non-ajax ) qui impliquent de nombreuses animations dans chacune d'elles. Actuellement, j'ai simplement une setTimeout entre les fonctions, mais ce n'est pas parfait, car aucun navigateur/ordinateur n'est identique.

Remarque supplémentaire: ils ont tous deux des animations/etc distinctes qui entrent en collision.

Je ne peux pas simplement en mettre un dans la fonction de rappel d'un autre

// multiple dom animations / etc
FunctionOne();

// What I -was- doing to wait till running the next function filled
// with animations, etc

setTimeout(function () { 
    FunctionTwo(); // other dom animations (some triggering on previous ones)
}, 1000); 

Y at-il de toute façon dans js/jQuery d'avoir:

// Pseudo-code
-do FunctionOne()
-when finished :: run -> FunctionTwo()

Je connais $.when() & $.done(), mais ce sont pour AJAX ...


  • MA SOLUTION MISE À JOUR

jQuery a une variable exposée ($ pour une raison quelconque qui ne figure nulle part dans les documents jQuery) appelée $ .timers, qui contient le tableau des animations en cours.

function animationsTest (callback) {
    // Test if ANY/ALL page animations are currently active

    var testAnimationInterval = setInterval(function () {
        if (! $.timers.length) { // any page animations finished
            clearInterval(testAnimationInterval);
            callback();
        }
    }, 25);
};

Utilisation de base:

// run some function with animations etc    
functionWithAnimations();

animationsTest(function () { // <-- this will run once all the above animations are finished

    // your callback (things to do after all animations are done)
    runNextAnimations();

});

Vous pouvez utiliser jQuery's $.Deferred

var FunctionOne = function () {
  // create a deferred object
  var r = $.Deferred();

  // do whatever you want (e.g. ajax/animations other asyc tasks)

  setTimeout(function () {
    // and call `resolve` on the deferred object, once you're done
    r.resolve();
  }, 2500);

  // return the deferred object
  return r;
};

// define FunctionTwo as needed
var FunctionTwo = function () {
  console.log('FunctionTwo');
};

// call FunctionOne and use the `done` method
// with `FunctionTwo` as it's parameter
FunctionOne().done(FunctionTwo);

vous pouvez également regrouper plusieurs dossiers différés:

var FunctionOne = function () {
  var
    a = $.Deferred(),
    b = $.Deferred();

  // some fake asyc task
  setTimeout(function () {
    console.log('a done');
    a.resolve();
  }, Math.random() * 4000);

  // some other fake asyc task
  setTimeout(function () {
    console.log('b done');
    b.resolve();
  }, Math.random() * 4000);

  return $.Deferred(function (def) {
    $.when(a, b).done(function () {
      def.resolve();
    });
  });
};

http://jsfiddle.net/p22dK/

109
Yoshi

ajoute ce qui suit à la fin de la première fonction

return $.Deferred().resolve();

appeler les deux fonctions comme si

functionOne().done(functionTwo);
13
quemeful

Avec la réponse de Yoshi, j'ai trouvé une autre solution très simple (type de rappel) pour les animations.

jQuery a une variable exposée (/ pour certaines raisons ne figure pas dans les documents jQuery) appelée $ .timers , qui contient le tableau des animations en cours.

function animationsTest (callback) {
    // Test if ANY/ALL page animations are currently active

    var testAnimationInterval = setInterval(function () {
        if (! $.timers.length) { // any page animations finished
            clearInterval(testAnimationInterval);
            callback();
        }
    }, 25);
};

Utilisation de base:

functionOne(); // one with animations

animationsTest(functionTwo);

J'espère que cela aide certaines personnes!

MISE À JOUR ECMAScript 6  

Cela utilise une nouvelle fonctionnalité de JavaScript appelée Promises

functionOne (). then (functionTwo);

1
quemeful

Est-ce ce que vous voulez dire, homme: http://jsfiddle.net/LF75a/

Vous aurez une fonction déclencher la fonction suivante et ainsi de suite, c’est-à-dire ajouter un autre appel de fonction, puis ajoutez votre functionONe au bas de celui-ci.

S'il te plaît, fais-moi savoir si j'ai oublié quelque chose, j'espère que cela correspond à la cause :)

ou this: Appelle une fonction après avoir terminé la fonction précédente

Code:

function hulk()
{
  // do some stuff...
}
function simpsons()
{
  // do some stuff...
  hulk();
}
function thor()
{
  // do some stuff...
  simpsons();
}
1
Tats_innit

Cette réponse utilise promises , une fonctionnalité JavaScript de la norme ECMAScript 6. Si votre plate-forme cible ne prend pas en charge promises, remplissez-la avec PromiseJs .

Vous pouvez obtenir l'objet Deferred créé par jQuery pour l'animation à l'aide de .promise() dans l'appel à l'animation. Envelopper ces Deferreds dans ES6 Promises donne un code beaucoup plus propre que l’utilisation de minuteries. 

Vous pouvez également utiliser directement Deferreds, mais cela est généralement déconseillé, car ils ne suivent pas la spécification Promises/A +. 

Le code résultant ressemblerait à ceci:

var p1 = Promise.resolve($('#Content').animate({ opacity: 0.5 }, { duration: 500, queue: false }).promise());
var p2 = Promise.resolve($('#Content').animate({ marginLeft: "-100px" }, { duration: 2000, queue: false }).promise());
Promise.all([p1, p2]).then(function () {
    return $('#Content').animate({ width: 0 }, { duration: 500, queue: false }).promise();
});

Notez que la fonction dans Promise.all() renvoie la promesse. C'est là que la magie se produit. Si, lors d'un appel then, une promesse est renvoyée, le prochain appel then attendra que cette promesse soit résolue avant de s'exécuter.

jQuery utilise une file d'attente d'animation pour chaque élément. Les animations sur le même élément sont donc exécutées de manière synchrone. Dans ce cas, vous n’auriez pas à utiliser de promesses du tout!

J'ai désactivé la file d'attente d'animation jQuery pour montrer comment cela fonctionnerait avec les promesses. 

Promise.all() prend un tableau de promesses et crée un nouveau Promise qui se termine lorsque toutes les promesses du tableau sont terminées. 

Promise.race() accepte également un éventail de promesses, mais se termine dès que le premier Promise est terminé.

1
Domysee

Vous pouvez utiliser les javascript Promise et async/await pour implémenter un appel synchronisé des fonctions.

Supposons que vous souhaitiez exécuter n nombre de fonctions de manière synchronisée stockées dans un tableau. Voici ma solution.

async function executeActionQueue(funArray) {
  var length = funArray.length;
  for(var i = 0; i < length; i++) {
    await executeFun(funArray[i]);
  }
};

function executeFun(fun) {
  return new Promise((resolve, reject) => {
    
    // Execute required function here
    
    fun()
      .then((data) => {
        // do required with data 
        resolve(true);
      })
      .catch((error) => {
      // handle error
        resolve(true);
      });
  })
};

executeActionQueue(funArray);

0
Nidhi Shah

Vous pouvez le faire via la fonction de rappel.

$('a.button').click(function(){
    if (condition == 'true'){
        function1(someVariable, function() {
          function2(someOtherVariable);
        });
    }
    else {
        doThis(someVariable);
    }
});

function function1 (param, rappel) { ...faire des trucs rappeler();} 

0
Zaheer Babar

Voici une solution pour n-call (fonction récursive) . https://jsfiddle.net/mathew11/5f3mu0f4/7/

function myFunction(array){
var r = $.Deferred();

if(array.length == 0){
    r.resolve();
    return r;
}

var element = array.shift();
// async task 
timer = setTimeout(function(){
    $("a").text($("a").text()+ " " + element);
    var resolving = function(){
        r.resolve();
    }

    myFunction(array).done(resolving);

 }, 500);

return r;
}

//Starting the function
var myArray = ["Hi", "that's", "just", "a", "test"];
var alerting = function (){window.alert("finished!")};
myFunction(myArray).done(alerting);
0
mathew11