Как я могу вызвать 3 функции для их выполнения один за другим?
Если мне нужно вызывать эти функции одну за другой,
$('#art1').animate({'width':'1000px'},1000);
$('#art2').animate({'width':'1000px'},1000);
$('#art3').animate({'width':'1000px'},1000);
Я знаю, в jQuery я мог бы сделать что-то вроде:
$('#art1').animate({'width':'1000px'},1000,'linear',function(){
$('#art2').animate({'width':'1000px'},1000,'linear',function(){
$('#art3').animate({'width':'1000px'},1000);
});
});
Но давайте предположим, что я не использую jQuery и хочу позвонить:
some_3secs_function(some_value);
some_5secs_function(some_value);
some_8secs_function(some_value);
Как мне следует вызывать эту функцию для выполнения some_3secs_function
, и ПОСЛЕ того, как вызов заканчивается, затем выполнить some_5secs_function
и ПОСЛЕ того, как вызов заканчивается, затем вызвать some_8secs_function
?
UPDATE:
Это все еще не работает:
(function(callback){
$('#art1').animate({'width':'1000px'},1000);
callback();
})((function(callback2){
$('#art2').animate({'width':'1000px'},1000);
callback2();
})(function(){
$('#art3').animate({'width':'1000px'},1000);
}));
Три анимации начинаются одновременно
Где моя ошибка?
Ответы
Ответ 1
В Javascript есть синхронные и асинхронные функции.
Синхронные функции
Большинство функций в Javascript являются синхронными. Если вы должны были вызвать несколько синхронных функций в строке
doSomething();
doSomethingElse();
doSomethingUsefulThisTime();
они будут выполняться по порядку. doSomethingElse
не запускается до завершения doSomething
. doSomethingUsefulThisTime
, в свою очередь, не запустится до завершения doSomethingElse
.
Асинхронные функции
Асинхронная функция, однако, не будет ждать друг друга. Давайте посмотрим на тот же самый пример кода, который у нас был выше, на этот раз, предполагая, что функции асинхронны
doSomething();
doSomethingElse();
doSomethingUsefulThisTime();
Функции будут инициализированы по порядку, но все они будут выполняться примерно в одно и то же время. Вы не можете последовательно предсказать, какой из них будет закончен первым: тот, который занимает наименьшее количество времени для выполнения, закончит сначала.
Но иногда вам нужны функции, которые асинхронны для выполнения по порядку, а иногда вам нужны функции, которые синхронно выполняются асинхронно. К счастью, это возможно при обратных вызовах и тайм-аутах соответственно.
Callbacks
Предположим, что у нас есть три асинхронные функции, которые мы хотим выполнить по порядку, some_3secs_function
, some_5secs_function
и some_8secs_function
.
Поскольку функции могут передаваться как аргументы в Javascript, вы можете передать функцию в качестве обратного вызова для выполнения после завершения функции.
Если мы создадим такие функции, как
function some_3secs_function(value, callback){
//do stuff
callback();
}
то вы можете вызвать его по порядку, например:
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.
});
});
});
Таймауты
В Javascript вы можете указать функцию для выполнения после определенного таймаута (в миллисекундах). Фактически это может привести к синхронному функционированию синхронных функций.
Если у нас есть три синхронные функции, мы можем выполнить их асинхронно с помощью функции setTimeout
.
setTimeout(doSomething, 10);
setTimeout(doSomethingElse, 10);
setTimeout(doSomethingUsefulThisTime, 10);
Это, однако, немного уродливо и нарушает принцип DRY [wikipedia]. Мы могли бы немного почистить это, создав функцию, которая принимает массив функций и тайм-аут.
function executeAsynchronously(functions, timeout) {
for(var i = 0; i < functions.length; i++) {
setTimeout(functions[i], timeout);
}
}
Это можно назвать так:
executeAsynchronously(
[doSomething, doSomethingElse, doSomethingUsefulThisTime], 10);
Итак, если у вас есть асинхронные функции, которые вы хотите выполнить синхронно, используйте обратные вызовы, а если у вас есть синхронные функции, которые вы хотите выполнить асинхронно, используйте таймауты.
Ответ 2
В этом ответе используется promises
, функция JavaScript стандарта ECMAScript 6
. Если ваша целевая платформа не поддерживает promises
, заполните его PromiseJs.
Посмотрите на мой ответ здесь Дождитесь завершения функции с анимацией, пока не запустите другую функцию, если хотите использовать анимацию jQuery
.
Вот как будет выглядеть ваш код с ES6 Promises
и 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());
});
Нормальные методы также могут быть помещены в 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
});
Метод then
выполняется, как только закончится Promise
. Обычно возвращаемое значение function
, переданное в then
, передается следующему как результат.
Но если возвращается Promise
, следующая функция then
ожидает завершения выполнения Promise
и получает результаты его (значение, которое передается в fulfill
).
Ответ 3
Похоже, вы не полностью понимаете разницу между выполнением функции синхронной и асинхронной.
Код, который вы указали в своем обновлении, сразу же выполняет каждую из ваших функций обратного вызова, которые, в свою очередь, сразу же запускают анимацию. Анимации, однако, выполняются асинхронно. Он работает следующим образом:
- Выполните шаг в анимации
- Вызовите
setTimeout
с помощью функции, содержащей следующий шаг анимации и задержку
- Проходит некоторое время
- Обратный вызов
setTimeout
выполняет
- Вернитесь к шагу 1
Это продолжается до тех пор, пока не завершится последний шаг анимации. Тем временем ваши синхронные функции уже давно завершены. Другими словами, ваш вызов функции animate
не занимает 3 секунды. Эффект моделируется с задержками и обратными вызовами.
Вам нужна очередь. Внутри jQuery ставит очередь анимации, выполняя только обратный вызов после завершения соответствующей анимации. Если ваш обратный вызов затем запускает другую анимацию, эффект заключается в том, что они выполняются последовательно.
В простейшем случае это эквивалентно следующему:
window.setTimeout(function() {
alert("!");
// set another timeout once the first completes
window.setTimeout(function() {
alert("!!");
}, 1000);
}, 3000); // longer, but first
Здесь имеется общая функция асинхронного цикла. Он будет вызывать данные функции в порядке, ожидая указанного количества секунд между ними.
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);
}
Использование:
loop(
function() { alert("sam"); },
function() { alert("sue"); });
Очевидно, вы могли бы изменить это, чтобы получить настраиваемые времена ожидания или немедленно выполнить первую функцию или прекратить выполнение, когда функция в цепочке возвращает false
или apply
функции в указанном контексте или что-то еще, что вы можете необходимо.
Ответ 4
Я полагаю, что библиотека async предоставит вам очень элегантный способ сделать это. В то время как promises и обратные вызовы могут немного затрудниться с juggle, async может дать аккуратные шаблоны, чтобы упростить ваш мыслительный процесс. Чтобы запускать функции в последовательном порядке, вам необходимо поместить их в async waterfall. В async lingo каждая функция называется task
, которая принимает некоторые аргументы и a callback
; которая является следующей функцией в последовательности. Основная структура будет выглядеть примерно так:
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
});
Я просто попытался кратко объяснить структуру здесь. Прочтите через водопад руководство для получения дополнительной информации, оно довольно хорошо написано.
Ответ 5
ваши функции должны принимать функцию обратного вызова, которая вызывается при завершении.
function fone(callback){
...do something...
callback.apply(this,[]);
}
function ftwo(callback){
...do something...
callback.apply(this,[]);
}
тогда использование будет выглядеть следующим образом:
fone(function(){
ftwo(function(){
..ftwo done...
})
});
Ответ 6
asec=1000;
setTimeout('some_3secs_function("somevalue")',asec*3);
setTimeout('some_5secs_function("somevalue")',asec*5);
setTimeout('some_8secs_function("somevalue")',asec*8);
Я не буду углубленно обсуждать setTimeout здесь, но:
- В этом случае я добавил код для выполнения в виде строки. это самый простой способ передать var в вашу функцию setTimeout-ed, но пуристы будут жаловаться.
- вы также можете передать имя функции без кавычек, но никакая переменная не может быть передана.
- ваш код не дожидается запускает setTimeout.
- Это может быть затруднительным, если вы сначала задумаетесь: из-за предыдущей точки, если вы передадите переменную из вашей вызывающей функции, эта переменная больше не будет существовать к моменту запуска триггеров таймаута - функция вызова будет иметь выполненный, и он исчез.
- Я, как известно, использовал анонимные функции, чтобы обойти все это, но может быть лучший способ,
Ответ 7
Поскольку вы отметили его с помощью javascript, я бы пошел с таймером, так как ваши имена функций - 3, 5 и 8 секунд. Итак, запустите свой таймер, за 3 секунды, вызовите первый, 5 секунд в секунду, 8 секунд - в третьем, затем, когда это будет сделано, остановите таймер.
Обычно в Javascript то, что у вас есть, является правильным для функций, запускаются один за другим, но поскольку похоже, что вы пытаетесь выполнить синхронизированную анимацию, таймер будет вашим лучшим выбором.
Ответ 8
//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)},
]);
Ответ 9
Вы также можете использовать promises следующим образом:
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);
});
});
Вам нужно будет сделать some_value
глобальным, чтобы получить доступ к нему изнутри .then
В качестве альтернативы, из внешней функции вы можете вернуть значение, которое будет использовать внутренняя функция, например:
one(some_value).then(function(return_of_one){
two(return_of_one).then(function(return_of_two){
three(return_of_two);
});
});
Ответ 10
Я использую функцию waitUntil на основе javascript setTimeout
/*
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
};
Это будет работать отлично, если ваши функции задают определенное условие true, которое вы могли бы опросить. Кроме того, он поставляется с таймаутами, которые предлагают вам альтернативы, если ваша функция не может что-то сделать (даже в течение определенного времени. Подумайте о обратной связи с пользователем!)
например,
doSomething();
waitUntil(function() { return doSomething_value===1;}, doSomethingElse);
waitUntil(function() { return doSomethingElse_value===1;}, doSomethingUseful);
Примечания
Дата вызывает приблизительные оценки таймаута. Для большей точности переключитесь на такие функции, как console.time(). Имейте в виду, что Date предлагает большую кросс-браузерную и устаревшую поддержку. Если вам не нужны точные миллисекундные измерения; не беспокойтесь или, наоборот, оберните его и предложите console.time(), когда браузер поддерживает его
Ответ 11
Если метод 1 должен быть выполнен после метода 2, 3, 4. Следующий фрагмент кода может быть решением для этого с использованием отложенного объекта в JavaScript.
function method1(){
var dfd = new $.Deferred();
setTimeout(function(){
console.log("Inside Method - 1");
method2(dfd);
}, 5000);
return dfd.promise();
}
function method2(dfd){
setTimeout(function(){
console.log("Inside Method - 2");
method3(dfd);
}, 3000);
}
function method3(dfd){
setTimeout(function(){
console.log("Inside Method - 3");
dfd.resolve();
}, 3000);
}
function method4(){
console.log("Inside Method - 4");
}
var call = method1();
$.when(call).then(function(cb){
method4();
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>