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

151

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

$('#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?

ОБНОВИТЬ:

Это все еще не работает:

(function(callback){
    $('#art1').animate({'width':'1000px'},1000);
    callback();
})((function(callback2){
    $('#art2').animate({'width':'1000px'},1000);
    callback2();
})(function(){
    $('#art3').animate({'width':'1000px'},1000);
}));

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

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

texai
источник
Вы имеете в виду, что функции будут вызываться ровно через 3 5 и 8 секунд или только одну за другой?
Trass Vasston
Я думаю, что вы просто не уверены в синхронном и асинхронном выполнении функций. Я обновил свой ответ ниже. Надеюсь, поможет.
Уэйн
Попробуйте это .. github.com/dineshkani24/queuecall
Динешкани

Ответы:

243

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

Это, однако, немного некрасиво и нарушает принцип СУХОГО [wikipedia] . Мы могли бы немного это исправить, создав функцию, которая принимает массив функций и время ожидания.

function executeAsynchronously(functions, timeout) {
  for(var i = 0; i < functions.length; i++) {
    setTimeout(functions[i], timeout);
  }
}

Это можно назвать так:

executeAsynchronously(
    [doSomething, doSomethingElse, doSomethingUsefulThisTime], 10);

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

Питер Олсон
источник
7
Это не задержит функции на 3,5 и 8 секунд, как показано в примере, они будут просто запускаться одна за другой.
Trass Vasston
1
@Peter - Подожди, я в замешательстве. Если это простые синхронные вызовы, выполнение которых занимает несколько секунд, то зачем нам это нужно?
Уэйн
9
@Peter - +1 для самого красивого, запутанного метода, который я когда-либо видел для вызова трех синхронных функций в последовательности.
Уэйн
4
Спасибо за умелое объяснение различий между асинхронными и синхронизирующими функциями js. Это многое объясняет.
Jnelson
2
Это НЕ правильно по следующим причинам: (1) все 3 тайм-аута разрешатся через 10 секунд, поэтому все 3 линии запускаются одновременно. (2) этот метод требует, чтобы вы знали заранее продолжительность и «запланировали» функции на будущее, вместо того, чтобы ожидать разрешения более ранних асинхронных функций в цепочке и того, что является триггером. --- вместо этого вы хотите использовать один из следующих ответов, используя либо обратные вызовы, обещания, либо асинхронную библиотеку.
нольстериск
37

Этот ответ использует promisesфункцию ECMAScript 6стандарта JavaScript . Если ваша целевая платформа не поддерживает 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возвращается a , следующая thenфункция ожидает Promiseзавершения выполнения и получает результаты (значение, которое передается fulfill).

Domysee
источник
Я знаю, что это полезно, но нашел код, который трудно понять, не ища реальный пример, чтобы дать ему некоторый контекст. Я нашел это видео на YouTube: youtube.com/watch?v=y5mltEaQxa0 - и написал источник из видео здесь drive.google.com/file/d/1NrsAYs1oaxXw0kv9hz7a6LjtOEb6x7z-/... Есть еще некоторые нюансы , как улов отсутствует в этот пример, который это развивает. (используйте другой идентификатор в строке getPostById () или попробуйте изменить имя автора, чтобы оно не совпадало с постом и т. д.)
JGFMK
20

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

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

  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функциям в указанном контексте, или к чему-либо еще, что вам может понадобиться.

Wayne
источник
14

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

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

Али
источник
1
Это действительно делает JS более терпимым.
Майк
9

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

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

}

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

тогда использование будет выглядеть так:

fone(function(){
  ftwo(function(){
   ..ftwo done...
  })
});
user406905
источник
4
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

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

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

Sorean
источник
2

//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)},
]);

Yuuya
источник
Можете ли вы объяснить, что это такое? Что связано с подчеркиванием? Что делает назначенная функция next?
Mtso
1
Я объясняю пример 2 с использованием jsfiddle. jsfiddle.net/mzsteyuy/3 Если вы позволите мне примерно объяснить, пример 2 - это короткий путь кода в jsfiddle. подчеркивание - Массив, элементы которого встречны (i) и функция next и функция [0] ~ [2].
Yuuya
1

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

    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);
       });
    });
Cuzox
источник
0

Я использую функцию «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 (), когда браузер поддерживает это

Бипбот фон Стерлинг
источник
0

Если метод 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>

Сахадеб Патро
источник