Вызов функции после завершения предыдущей функции


179

У меня есть следующий код JavaScript:

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

Как я могу убедиться, что function2вызов вызывается только после function1завершения?


24
является function1выполнение операции асинхронной?
LiraNuna

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

Может кто-нибудь объяснить мне, почему нужно что-то сделать, чтобы функция2 не вызывалась до тех пор, пока функция1 не будет завершена? Здесь не происходит асинхронная операция, поэтому функция 2 не будет работать до тех пор, пока функция 1 не будет завершена, поскольку эта операция является синхронной.
Аарон

Ответы:


206

Укажите анонимный обратный вызов и заставьте function1 принять его:

$('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, он может просто использовать новый паттерн Deferreds
philwinkle

Спасибо за быстрый ответ (ы). Да, function1 просто выполняет простую асинхронную задачу, поэтому я считаю, что вы правы в том, что мне придется создать обратный вызов - или взглянуть на этот отложенный бизнес, о котором упоминал philwinkle. Еще раз спасибо, ребята. Я собираюсь немного поспать и снова заняться этим утром.
Rrryyyaaannn

13
Этот ответ не является решением. Вот доказательство того, что это не работает: jsfiddle.net/trusktr/M2h6e
trusktr

12
@MikeRobinson Вот проблема: что если в ...do stuffней есть асинхронные объекты? Функция 2 по-прежнему не сработает, как ожидается. Пример в моем комментарии выше показывает, что, поскольку foo()функция содержит асинхронный код, bar()она не запускает свое содержимое после выполнения foo()содержимого, даже используя $.when().then()для вызова их один за другим. Этот ответ действителен только в том случае, если (и только если) все содержимое ...do stuffвашего кода строго синхронно.
trusktr

1
@trusktr В этом случае вам нужно продолжать передавать первый обратный вызов до n- го уровня асинхронного вызова, а затем запускаться callBack()после выполнения всех n асинхронных вызовов. Поскольку OP не упомянул, что он делает в функции, он принял это как одноуровневый асинхронный вызов.
GoodSp33d

96

Если вы используете jQuery 1.5, вы можете использовать новый шаблон Deferreds:

$('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
Но это рабочий пример? Что откладывается? Вы выполняете function1 и function2 немедленно. (Я не оригинальный комментатор.)
user113716

10
Это не работает для меня. Функция function1 и function2 выполняются в одно и то же время (что можно наблюдать человеческим глазом). Вот небольшой пример: jsfiddle.net/trusktr/M2h6e
trusktr

1
Запись Ребекки Мерфи была до того, как Defferds были представлены в jQuery, и она использует примеры, основанные на том, как автор думает, что это будет реализовано. Пожалуйста, посетите сайт jQuery, чтобы увидеть, как на самом деле использовать эту функцию: api.jquery.com/jQuery.Deferred
Спенсер Уильямс

1
Даже с jQuery 1.5 или чем-то современным, не правда ли $.when(function1).then(function2);(без скобок, вызывающих функцию)?
Teepeemm

1
Чтение этих комментариев на несколько лет позже. function1должен вернуть обещание. В этом случае это должна быть фактическая выполняемая функция, а не ссылка. Когда resolveвызывается то обещание, то function2выполняется. Это легко объяснить, если предположить, что function1есть вызов ajax. doneОбратный вызов будет затем решить обещание. Надеюсь, это понятно.
Philwinkle

46

Попробуй это :

function method1(){
   // some code

}

function method2(){
   // some code
}

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

37

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

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

На самом деле в jQuery версии 3+ совместим с Promises A +. Безотносительно $.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

Это зависит от того, что делает function1.

Если function1 выполняет какой-то простой синхронный javascript, такой как обновление значения div или чего-то еще, то function2 сработает после завершения function1.

Если 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.