Виклик функції після завершення попередньої функції


179

У мене є такий код JavaScript:

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

Як я можу забезпечити function2виклик лише після function1завершення?


24
є function1виконання операції асинхронної?
LiraNuna

8
Yads, якщо function1 містить анімацію, function2 буде виконуватися, поки анімації function1 все ще відбуваються. Як би ви змусили функцію2 чекати, коли все, що розпочалося в функції1, повністю виконано?
trusktr

Чи може хтось пояснити мені, чому потрібно щось робити для того, щоб функція2 не була викликана до завершення функції1? Тут не відбувається жодної асинхронної операції, тому function2 не буде запускатися, поки функція1 все одно не буде завершена, оскільки ця операція є синхронною.
Аарон

Відповіді:


206

Вкажіть анонімний зворотний дзвінок і змусьте функцію1 прийняти його:

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


function function1(param, callback) {
  ...do stuff
  callback();
} 

11
+1, але якщо він використовує 1,5, він може просто використовувати новий шаблон відкладеного
philwinkle

Дякуємо за швидку відповідь. Так, функція1 просто виконує просту асинхронну задачу, тому я вважаю, що ви правильні в тому, що мені доведеться створити зворотний дзвінок - або подивіться на цей відкладений бізнес, про який згадував philwinkle. Ще раз дякую, хлопці. Я буду трохи поспати, і вранці займатись цим ще раз.
Rrryyyaaannn

13
Ця відповідь не є рішенням. Ось доказ, що він не працює: jsfiddle.net/trusktr/M2h6e
trusktr

12
@MikeRobinson Ось проблема: Що робити, якщо ...do stuffмістяться асинхронні речі? Функція2 все одно не запуститься, коли очікується. Приклад у моєму коментарі вище показує, що оскільки foo()функція має асинхронний код у ній, bar()не запускає вміст після того, як вміст foo()'s буде виконано, навіть використовуючи $.when().then()для виклику їх один за одним. Ця відповідь справедлива лише в тому випадку, якщо (і лише якщо) всі речі ...do stuffу вашому коді суворо синхронні.
trusktr

1
@trusktr У цьому випадку вам потрібно продовжувати передачу першого зворотного дзвінка до n- го рівня асинхронного виклику, після чого виконувати запуск callBack()після всіх n асинхронних дзвінків. Оскільки ОП не згадував про те, що він робить у своїй функції, він вважав це асинхронним викликом одного рівня.
GoodSp33d

96

Якщо ви використовуєте jQuery 1.5, ви можете використовувати новий шаблон відкладених дій:

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

Редагувати: оновлене посилання на щоденник:

Ребекка Мерфі чудово описувала тут: http://rmurphey.com/blog/2010/12/25/deferreds-coming-to-jquery/


2
Але це робочий приклад? Що відкладається? Ви негайно виконуєте функцію1 та function2. (Я не оригінальний коментатор.)
user113716

10
Це для мене зовсім не працює. function1 та function2 виконуються точно в один і той же час (як це спостерігається людським оком). Ось крихітний приклад: jsfiddle.net/trusktr/M2h6e
trusktr

1
Запис Ребекки Мерфі був до того, як Деффердс був представлений в jQuery, і він використовує приклади, засновані на тому, як письменник думає, що це буде реалізовано. Завітайте на сайт jQuery, щоб дізнатися, як реально використовувати цю функцію: api.jquery.com/jQuery.Deferred
Спенсер Вільямс

1
Навіть із jQuery 1.5 чи чимось сучасним, ви не хочете $.when(function1).then(function2);(без дужок, які викликають функцію)?
Teepeemm

1
Читання цих коментарів запізнилося на кілька років. function1повинні повернути обіцянку. У цьому випадку це повинна бути фактично виконувана функція, а не посилання. Коли resolveпокликана ця обіцянка, то function2виконується. Це легко пояснити, якщо припустити, що function1має виклик аякса. Тоді doneзворотний виклик вирішить обіцянку. Я сподіваюся, що це зрозуміло.
philwinkle

46

Спробуйте це :

function method1(){
   // some code

}

function method2(){
   // some code
}

$.ajax({
   url:method1(),
   success:function(){
   method2();
}
})

37

Ця відповідь використовує promisesфункцію JavaScript ECMAScript 6стандарту. Якщо ваша цільова платформа не підтримує promises, заповніть її за допомогою PromiseJs .

Обіцяння - це новий (і набагато кращий) спосіб обробки асинхронних операцій у JavaScript:

$('a.button').click(function(){
    if (condition == 'true'){
        function1(someVariable).then(function() {
            //this function is executed after function1
            function2(someOtherVariable);
        });
    }
    else {
        doThis(someVariable);
    }
});


function function1(param, callback) {
    return new Promise(function (fulfill, reject){
        //do stuff
        fulfill(result); //if the action succeeded
        reject(error); //if the action did not succeed
    });
} 

Це може здатися суттєвим накладним набором для цього простого прикладу, але для більш складного коду це набагато краще, ніж використання зворотних викликів. Ви можете легко ланцюг кількох асинхронних дзвінків, використовуючи кілька thenоператорів:

function1(someVariable).then(function() {
    function2(someOtherVariable);
}).then(function() {
    function3();
});

Ви також можете легко обернути відкладені jQuery (які повертаються з $.ajaxдзвінків):

Promise.resolve($.ajax(...params...)).then(function(result) {
    //whatever you want to do after the request
});

Як зазначав @charlietfl, jqXHRоб'єкт, що повертається, $.ajax()реалізує Promiseінтерфейс. Тому насправді обгортати його не потрібно Promise, це можна використовувати безпосередньо:

$.ajax(...params...).then(function(result) {
    //whatever you want to do after the request
});

Promise.resolveобгортання $.ajaxє анти-візерунком, оскільки $.ajaxповертає обіцянку, що тоді
можливо

@charlietfl, але це не є фактичним Promise, він просто реалізує інтерфейс. Я не впевнений, як він поводиться, передаючи реальний Promiseсвоєму thenметоду, або що Promise.allб зробив, якщо а jqXHRі а Promiseпередаються йому. Для цього мені потрібно провести подальше тестування. Але дякую за підказку, я додам це до відповіді!
Доміссей

Насправді у версії jQuery 3+ передбачено сумісність Promises A +. Irregardless $.ajax.thenне новий
charlietfl

21

Або ви можете запустити власну подію, коли одна функція буде виконана, а потім прив’яжіть її до документа:

function a() {
    // first function code here
    $(document).trigger('function_a_complete');
}

function b() {
    // second function code here
}

$(document).bind('function_a_complete', b);

Використовуючи цей метод, функція "b" може виконувати лише ПІСЛЯ функцію "a", оскільки тригер існує лише тоді, коли функцію a закінчено виконувати.


"Станом на jQuery 1.7, метод .on () є кращим методом приєднання обробників подій до документа." api.jquery.com/bind
CodeVirtuoso


3

Це залежить від того, яку функцію1 виконує.

Якщо function1 робить якийсь простий синхронний javascript, наприклад оновлення значення div або щось подібне, функція2 запуститься після завершення функції1.

Якщо function1 здійснює асинхронний виклик, такий як AJAX-виклик, вам потрібно буде створити метод "зворотного дзвінка" (більшість API-сервера ajax мають параметр функції зворотного виклику). Потім виклик function2 у зворотному дзвінку. наприклад:

function1()
{
  new AjaxCall(ajaxOptions, MyCallback);
}

function MyCallback(result)
{
  function2(result);
}

2

Якщо метод 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>


0

Якщо function1 - це якась функція синхронізації, яку ви хочете перетворити на функцію асинхронізації, тому що потрібно певний час, і ви не маєте ніякого контролю над цим, щоб додати зворотній зв'язок:

function function1 (someVariable) {
    var date = Date.now ();
    while (Date.now () - date < 2000);      // function1 takes some time to complete
    console.log (someVariable);
}
function function2 (someVariable) {
    console.log (someVariable);
}
function onClick () {
    window.setTimeout (() => { function1 ("This is function1"); }, 0);
    window.setTimeout (() => { function2 ("This is function2"); }, 0);
    console.log ("Click handled");  // To show that the function will return before both functions are executed
}
onClick ();

Вихід буде:

Click handled

... і через 2 секунди:

This is function 1
This is function 2

Це працює, тому що виклик window.setTimeout () додасть завдання в цикл завдань JS виконуваного завдання, який викликає асинхронний виклик, і тому що основний принцип "запуску до завершення" виконання JS забезпечує, що onClick () ніколи не переривається, перш ніж закінчиться.

Зауважте, що це так смішно, як це може бути кодом важко зрозуміти ...

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.