Si j’ai besoin d’appeler cette fonction l’une après l’autre,
$('#art1').animate({'width':'1000px'},1000);
$('#art2').animate({'width':'1000px'},1000);
$('#art3').animate({'width':'1000px'},1000);
Je sais que dans jQuery je pourrais faire quelque chose comme:
$('#art1').animate({'width':'1000px'},1000,'linear',function(){
$('#art2').animate({'width':'1000px'},1000,'linear',function(){
$('#art3').animate({'width':'1000px'},1000);
});
});
Mais supposons que je n'utilise pas jQuery et que je souhaite appeler:
some_3secs_function(some_value);
some_5secs_function(some_value);
some_8secs_function(some_value);
Comment appeler ces fonctions pour exécuter some_3secs_function
, APRES la fin de cet appel, puis exécuter some_5secs_function
et APRES la fin de cet appel, puis appeler some_8secs_function
?
PDATE:
Cela ne fonctionne toujours pas:
(function(callback){
$('#art1').animate({'width':'1000px'},1000);
callback();
})((function(callback2){
$('#art2').animate({'width':'1000px'},1000);
callback2();
})(function(){
$('#art3').animate({'width':'1000px'},1000);
}));
Trois animations commencent en même temps
Où est mon erreur?
En Javascript, il existe des fonctions synchrones et asynchrones .
La plupart des fonctions en Javascript sont synchrones. Si vous deviez appeler plusieurs fonctions synchrones à la suite
doSomething();
doSomethingElse();
doSomethingUsefulThisTime();
ils exécuteront dans l'ordre. doSomethingElse
ne démarrera pas tant que doSomething
ne sera pas terminé. doSomethingUsefulThisTime
, à son tour, ne commencera pas tant que doSomethingElse
ne sera pas terminé.
La fonction asynchrone ne s’attendra cependant pas. Voyons le même exemple de code que ci-dessus, cette fois en supposant que les fonctions sont asynchrones
doSomething();
doSomethingElse();
doSomethingUsefulThisTime();
Les fonctions seront initialisées dans l’ordre, mais elles seront toutes exécutées à peu près en même temps. Vous ne pouvez pas toujours prédire laquelle finira en premier: celle qui prendra le moins de temps possible sera exécutée en premier.
Mais parfois, vous souhaitez que les fonctions asynchrones s'exécutent dans l'ordre, et parfois, les fonctions synchrones s'exécutent de manière asynchrone. Heureusement, cela est possible avec les rappels et les délais d'attente, respectivement.
Supposons que nous souhaitons exécuter trois fonctions asynchrones dans l'ordre, some_3secs_function
, some_5secs_function
et some_8secs_function
.
Comme les fonctions peuvent être passées sous forme d'arguments en Javascript, vous pouvez passer une fonction en tant que rappel à exécuter une fois la fonction terminée.
Si on crée les fonctions comme ça
function some_3secs_function(value, callback){
//do stuff
callback();
}
alors vous pouvez appeler ensuite dans l'ordre, comme ceci:
some_3secs_function(some_value, function() {
some_5secs_function(other_value, function() {
some_8secs_function(third_value, function() {
//All three functions have completed, in order.
});
});
});
En Javascript, vous pouvez indiquer à une fonction de s'exécuter après un certain délai (en millisecondes). Cela peut en effet rendre les fonctions synchrones se comporter de manière asynchrone.
Si nous avons trois fonctions synchrones, nous pouvons les exécuter de manière asynchrone à l'aide de la fonction setTimeout
.
setTimeout(doSomething, 10);
setTimeout(doSomethingElse, 10);
setTimeout(doSomethingUsefulThisTime, 10);
Ceci est cependant un peu moche et viole le principe principe DRY[Wikipédia] . Nous pourrions nettoyer cela un peu en créant une fonction qui accepte un tableau de fonctions et un délai d'attente.
function executeAsynchronously(functions, timeout) {
for(var i = 0; i < functions.length; i++) {
setTimeout(functions[i], timeout);
}
}
Cela peut s'appeler comme suit:
executeAsynchronously(
[doSomething, doSomethingElse, doSomethingUsefulThisTime], 10);
En résumé, si vous avez des fonctions asynchrones que vous souhaitez exécuter de manière syncrone, utilisez des rappels, et si vous avez des fonctions synchrones que vous souhaitez exécuter de manière asynchrone, utilisez des délais d'attente.
promises
, une fonctionnalité JavaScript de la norme ECMAScript 6
. Si votre plate-forme cible ne prend pas en charge promises
, remplissez-la avec PromiseJs .Regardez ma réponse ici Attendez qu'une fonction avec des animations soit terminée avant d'exécuter une autre fonction si vous souhaitez utiliser des animations jQuery
.
Voici à quoi ressemblerait votre code avec ES6 Promises
et jQuery animations
.
Promise.resolve($('#art1').animate({ 'width': '1000px' }, 1000).promise()).then(function(){
return Promise.resolve($('#art2').animate({ 'width': '1000px' }, 1000).promise());
}).then(function(){
return Promise.resolve($('#art3').animate({ 'width': '1000px' }, 1000).promise());
});
Les méthodes normales peuvent également être encapsulées dans Promises
.
new Promise(function(fulfill, reject){
//do something for 5 seconds
fulfill(result);
}).then(function(result){
return new Promise(function(fulfill, reject){
//do something for 5 seconds
fulfill(result);
});
}).then(function(result){
return new Promise(function(fulfill, reject){
//do something for 8 seconds
fulfill(result);
});
}).then(function(result){
//do something with the result
});
La méthode then
est exécutée dès que Promise
est terminé. Normalement, la valeur de retour de function
passée à then
est transmise à la suivante comme résultat.
Mais si un Promise
est renvoyé, la fonction then
suivante attend jusqu'à ce que Promise
ait fini de s'exécuter et en reçoive les résultats (la valeur transmise à fulfill
).
Il semble que vous n'appréciez pas pleinement la différence entre l'exécution de la fonction synchrone et asynchrone.
Le code que vous avez fourni dans votre mise à jour immédiatement exécute chacune de vos fonctions de rappel, ce qui démarre immédiatement une animation. Cependant, les animations s'exécutent de manière asyncronante . Cela fonctionne comme ceci:
setTimeout
avec une fonction contenant l'étape d'animation suivante et un délaisetTimeout
s'exécuteCela continue jusqu'à la fin de la dernière étape de l'animation. Entre-temps, vos fonctions synchrones sont terminées depuis longtemps. En d'autres termes, votre appel à la fonction animate
ne prend pas réellement 3 secondes. L'effet est simulé avec des retards et des rappels.
Ce dont vous avez besoin est un file d'attente. En interne, jQuery met en file d'attente les animations, n'exécutant votre rappel qu'une fois l'animation correspondante terminée. Si votre rappel démarre alors une autre animation, elles sont exécutées en séquence.
Dans le cas le plus simple, cela équivaut à ce qui suit:
window.setTimeout(function() {
alert("!");
// set another timeout once the first completes
window.setTimeout(function() {
alert("!!");
}, 1000);
}, 3000); // longer, but first
Voici une fonction générale de bouclage asynchrone. Il appellera les fonctions données dans l’ordre, en attendant le nombre de secondes spécifié.
function loop() {
var args = arguments;
if (args.length <= 0)
return;
(function chain(i) {
if (i >= args.length || typeof args[i] !== 'function')
return;
window.setTimeout(function() {
args[i]();
chain(i + 1);
}, 2000);
})(0);
}
Usage:
loop(
function() { alert("sam"); },
function() { alert("sue"); });
Vous pouvez évidemment modifier ceci pour prendre des temps d’attente configurables ou pour exécuter immédiatement la première fonction ou pour arrêter de l’exécuter lorsqu'une fonction de la chaîne renvoie false
ou à apply
les fonctions d’un contexte spécifié ou autre. va peut-être avoir besoin de.
Je crois que la bibliothèque asynchrone vous fournira un moyen très élégant de le faire. Alors que les promesses et les rappels peuvent être un peu difficiles à jongler, async peut donner des schémas ordonnés pour rationaliser votre processus de pensée. Pour exécuter des fonctions en série, vous devez les placer en mode asynchrone cascade . Dans le jargon asynchrone, chaque fonction est appelée une task
qui prend des arguments et une callback
; qui est la fonction suivante de la séquence. La structure de base ressemblerait à quelque chose comme:
async.waterfall([
// A list of functions
function(callback){
// Function no. 1 in sequence
callback(null, arg);
},
function(arg, callback){
// Function no. 2 in sequence
callback(null);
}
],
function(err, results){
// Optional final callback will get results for all prior functions
});
Je viens d'essayer d'expliquer brièvement la structure ici. Lisez la cascade guide pour plus d'informations, c'est assez bien écrit.
vos fonctions doivent prendre une fonction de rappel, qui est appelée à la fin.
function fone(callback){
...do something...
callback.apply(this,[]);
}
function ftwo(callback){
...do something...
callback.apply(this,[]);
}
alors l'utilisation serait comme:
fone(function(){
ftwo(function(){
..ftwo done...
})
});
asec=1000;
setTimeout('some_3secs_function("somevalue")',asec*3);
setTimeout('some_5secs_function("somevalue")',asec*5);
setTimeout('some_8secs_function("somevalue")',asec*8);
Je n'entrerai pas dans une discussion approfondie de setTimeout ici, mais:
Puisque vous l'avez taguée avec javascript, j'utiliserais un contrôle de minuterie puisque les noms de vos fonctions sont 3, 5 et 8 secondes. Alors lancez votre chronomètre, 3 secondes avant, appelez le premier, 5 secondes en appelez le second, 8 secondes en appelez le troisième, puis lorsque c'est terminé, arrêtez le chronomètre.
Normalement, en Javascript, ce que vous avez est correct car les fonctions sont exécutées les unes après les autres, mais comme il semble que vous essayez de réaliser une animation chronométrée, une minuterie serait votre meilleur choix.
//sample01
(function(_){_[0]()})([
function(){$('#art1').animate({'width':'10px'},100,this[1].bind(this))},
function(){$('#art2').animate({'width':'10px'},100,this[2].bind(this))},
function(){$('#art3').animate({'width':'10px'},100)},
])
//sample02
(function(_){_.next=function(){_[++_.i].apply(_,arguments)},_[_.i=0]()})([
function(){$('#art1').animate({'width':'10px'},100,this.next)},
function(){$('#art2').animate({'width':'10px'},100,this.next)},
function(){$('#art3').animate({'width':'10px'},100)},
]);
//sample03
(function(_){_.next=function(){return _[++_.i].bind(_)},_[_.i=0]()})([
function(){$('#art1').animate({'width':'10px'},100,this.next())},
function(){$('#art2').animate({'width':'10px'},100,this.next())},
function(){$('#art3').animate({'width':'10px'},100)},
]);
Vous pouvez également utiliser les promesses de cette manière:
some_3secs_function(this.some_value).then(function(){
some_5secs_function(this.some_other_value).then(function(){
some_8secs_function(this.some_other_other_value);
});
});
Vous devez rendre some_value
global pour y accéder de l'intérieur du fichier .then
Vous pouvez également renvoyer, à partir de la fonction externe, la valeur que la fonction interne utiliserait, comme suit:
one(some_value).then(function(return_of_one){
two(return_of_one).then(function(return_of_two){
three(return_of_two);
});
});
J'utilise une fonction 'waitUntil' basée sur setTimeout de javascript
/*
funcCond : function to call to check whether a condition is true
readyAction : function to call when the condition was true
checkInterval : interval to poll <optional>
timeout : timeout until the setTimeout should stop polling (not 100% accurate. It was accurate enough for my code, but if you need exact milliseconds, please refrain from using Date <optional>
timeoutfunc : function to call on timeout <optional>
*/
function waitUntil(funcCond, readyAction, checkInterval, timeout, timeoutfunc) {
if (checkInterval == null) {
checkInterval = 100; // checkinterval of 100ms by default
}
var start = +new Date(); // use the + to convert it to a number immediatly
if (timeout == null) {
timeout = Number.POSITIVE_INFINITY; // no timeout by default
}
var checkFunc = function() {
var end = +new Date(); // rough timeout estimations by default
if (end-start > timeout) {
if (timeoutfunc){ // if timeout function was defined
timeoutfunc(); // call timeout function
}
} else {
if(funcCond()) { // if condition was met
readyAction(); // perform ready action function
} else {
setTimeout(checkFunc, checkInterval); // else re-iterate
}
}
};
checkFunc(); // start check function initially
};
Cela fonctionnerait parfaitement si vos fonctions définissaient une certaine condition sur true, que vous seriez en mesure d’examiner. De plus, il comporte des délais d'expiration, qui vous offrent des alternatives au cas où votre fonction n'agirait pas (même dans le délai imparti. Pensez aux commentaires des utilisateurs!).
par exemple
doSomething();
waitUntil(function() { return doSomething_value===1;}, doSomethingElse);
waitUntil(function() { return doSomethingElse_value===1;}, doSomethingUseful);
Notes
La date provoque des estimations approximatives de délai d'attente. Pour plus de précision, passez à des fonctions telles que console.time (). Notez que Date offre une meilleure prise en charge multi-navigateurs et héritée. Si vous n'avez pas besoin de mesures exactes en millisecondes; ne vous embêtez pas, ou bien emballez-le et proposez console.time () lorsque le navigateur le supporte