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

179

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

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

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

Rrryyyaaannn
источник
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/

philwinkle
источник
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();
}
})
TuanTruong
источник
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
});
Domysee
источник
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
5

Вы можете сделать это так

$.when(funtion1()).then(function(){
    funtion2();
})
Джей Момая
источник
js.do/code/465839 хорошо, работает ,,
Буди Мулё
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 () никогда не прерывается до его окончания.

Обратите внимание, что это так смешно, как может быть, код трудно понять ...

StashOfCode
источник