Как мне вызвать 3 функции, чтобы выполнять их одну за другой? - PullRequest
131 голосов
/ 04 марта 2011

Если мне нужно вызывать эти функции одну за другой,

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

Три анимации начинаются одновременно

Где моя ошибка.

Ответы [ 10 ]

224 голосов
/ 04 марта 2011

В Javascript есть синхронные и асинхронные функции.

Синхронные функции

Большинство функций в Javascript являются синхронными.Если вам нужно было вызвать несколько синхронных функций подряд

doSomething();
doSomethingElse();
doSomethingUsefulThisTime();

, они будут выполняться по порядку.doSomethingElse не запустится, пока не завершится doSomething.doSomethingUsefulThisTime, в свою очередь, не запустится, пока не завершится * 1015. *

Асинхронные функции

Однако асинхронная функция не будет ждать друг друга.Давайте посмотрим на тот же пример кода, который мы имели выше, на этот раз, предполагая, что функции асинхронны

doSomething();
doSomethingElse();
doSomethingUsefulThisTime();

Функции будут инициализированы по порядку, но все они будут выполняться примерно в одно и то же время.Вы не можете однозначно предсказать, какой из них закончится первым: тот, который занимает наименьшее количество времени для выполнения, закончится первым.

Но иногда вы хотите, чтобы функции, выполняемые асинхронно, выполнялись по порядку, ииногда вы хотите, чтобы синхронные функции выполнялись асинхронно.К счастью, это возможно с обратными вызовами и тайм-аутами соответственно.

Обратные вызовы

Предположим, что у нас есть три асинхронные функции, которые мы хотим выполнить по порядку, 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);

В итоге, если у вас есть асинхронные функции, которые вы хотите выполнять синхронно, используйте обратные вызовы, и если у вас есть синхронные функции, которыеВы хотите выполнить асинхронно, используйте тайм-ауты.

32 голосов
/ 23 сентября 2015

В этом ответе используется 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).

20 голосов
/ 04 марта 2011

Похоже, вы не до конца понимаете разницу между синхронным и асинхронным выполнением функции.

Код, который вы указали в своем обновлении немедленно , выполняет все функции обратного вызова, которые, в свою очередь, немедленно запускают анимацию. Анимации, однако, выполняются асинхронно . Это работает так:

  1. Выполните шаг в анимации
  2. Вызов setTimeout с функцией, содержащей следующий шаг анимации и задержкой
  3. проходит некоторое время
  4. Обратный вызов, данный setTimeout, выполняет
  5. Вернуться к шагу 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 функции в указанном контексте или что-либо еще, что вы могли бы необходимо.

10 голосов
/ 14 января 2016

Я считаю, что библиотека async предоставит вам очень элегантный способ сделать это.В то время как обещания и обратные вызовы могут быть немного трудными для манипулирования, асинхронность может дать аккуратные шаблоны для оптимизации вашего мыслительного процесса.Чтобы запустить функции в последовательном режиме, вам нужно поместить их в асинхронный водопад .В асинхронном жаргоне каждая функция называется task, которая принимает некоторые аргументы, и 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
});

Я только что попытался кратко объяснить структуру здесь.Прочитайте руководство водопада для получения дополнительной информации, оно довольно хорошо написано.

8 голосов
/ 04 марта 2011

ваши функции должны принимать функцию обратного вызова, которая вызывается по завершении.

function fone(callback){
...do something...
callback.apply(this,[]);

}

function ftwo(callback){
...do something...
callback.apply(this,[]);
}

тогда использование будет выглядеть следующим образом:

fone(function(){
  ftwo(function(){
   ..ftwo done...
  })
});
4 голосов
/ 04 марта 2011
asec=1000; 

setTimeout('some_3secs_function("somevalue")',asec*3);
setTimeout('some_5secs_function("somevalue")',asec*5);
setTimeout('some_8secs_function("somevalue")',asec*8);

Я не буду здесь углубленно обсуждать setTimeout, но:

  • в этом случае я добавил код для выполнения в виде строки. это самый простой способ передать переменную в функцию setTimeout-ed, но пуристы будут жаловаться.
  • Вы также можете передать имя функции без кавычек, но переменная не может быть передана.
  • ваш код не ждет срабатывания setTimeout.
  • Поначалу может быть трудно разобраться: из-за предыдущего пункта, если вы передадите переменную из вызывающей функции, эта переменная больше не будет существовать к тому времени, когда истечет время ожидания - вызывающая функция будет иметь казнены, и его нет.
  • Мне известно, что я использую анонимные функции, чтобы обойти все это, но вполне может быть лучший способ,
3 голосов
/ 04 марта 2011

Поскольку вы пометили его с помощью javascript, я бы использовал элемент управления таймером, поскольку ваши имена функций составляют 3, 5 и 8 секунд.Итак, запустите таймер, 3 секунды, вызовите первую, 5 секунд - вызов второй, 8 секунд - третий, затем, когда все будет сделано, остановите таймер.

Обычно в Javascript все правильнопотому что функции выполняются одна за другой, но, похоже, вы пытаетесь сделать синхронизированную анимацию, лучше всего подойдет таймер.

2 голосов
/ 14 ноября 2016

//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)},
]);
1 голос
/ 28 июля 2016

Вы также можете использовать обещания следующим образом:

    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);
       });
    });
0 голосов
/ 03 августа 2017

Я использую функцию 'waitUntil', основанную на setTimeout 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
};

Это будет прекрасно работать, если ваши функции установят для определенного условия значение true, которое вы сможете опрашивать.Кроме того, он поставляется с тайм-аутами, которые предлагают вам альтернативы в случае, если ваша функция не смогла что-то сделать (даже в пределах временного диапазона. Подумайте об обратной связи с пользователем!)

например

doSomething();
waitUntil(function() { return doSomething_value===1;}, doSomethingElse);
waitUntil(function() { return doSomethingElse_value===1;}, doSomethingUseful);

Примечания

Дата приводит к приблизительным оценкам времени ожидания.Для большей точности переключитесь на такие функции, как console.time ().Обращаем ваше внимание, что Date предлагает более широкую поддержку браузеров и устаревших версий.Если вам не нужны точные миллисекундные измерения;не беспокойтесь, или, альтернативно, не оборачивайте это, и предлагайте console.time (), когда браузер поддерживает это

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...