вызовы функций последовательности в javascript - единственный способ - обратные вызовы?

Я читал различные темы, например, эту.

Но это действительно ускользает от меня, как выполнить следующее:

У меня есть 4 функции, и я хочу, чтобы они выполнялись одна за другой последовательно. Обратите внимание, что они в неправильном порядке, чтобы понять мою точку зрения. Мне нужен результат, который выведет «1, 2, 3, 4».

function firstFunction(){
  // some very time consuming asynchronous code...
  console.log('1');
}
function thirdFunction(){
  // definitely dont wanna do this until secondFunction is finished
  console.log('3');
}
function secondFunction(){
  // waits for firstFunction to be completed
  console.log('2');
}
function fourthFunction(){
  // last function, not executed until the other 3 are done.
  console.log('4');
}

Я пытался выяснить обратные вызовы, но теряюсь :(

Нет ли какого-нибудь простого способа сделать это? Как цикл по массиву...


person tim    schedule 05.09.2012    source источник
comment
Вам может быть интересно взглянуть на промисы и отложенные объекты jQuery.   -  person apsillers    schedule 05.09.2012


Ответы (5)


Это отличный шанс начать использовать jQuery Deferred.

Помимо решения на основе обратных вызовов, код удобочитаем, гибок и прост в сопровождении.

http://jsfiddle.net/zerkms/zJhph/

function firstFunction(){
  var d = $.Deferred();
  // some very time consuming asynchronous code...
  setTimeout(function() {
    console.log('1');
    d.resolve();
  }, 1000);
  return d.promise();
}
function thirdFunction(){
  var d = $.Deferred();
  // definitely dont wanna do this until secondFunction is finished
  setTimeout(function() {
    console.log('3');
    d.resolve();
  }, 1000);
  return d.promise();
}
function secondFunction(){
  var d = $.Deferred();
  setTimeout(function() {
    console.log('2');
    d.resolve();
  }, 1000);
  return d.promise();
}
function fourthFunction(){
  var d = $.Deferred();
  // last function, not executed until the other 3 are done.
  setTimeout(function() {
    console.log('4');
    d.resolve();
  }, 1000);
  return d.promise();
}

firstFunction().pipe(secondFunction).pipe(thirdFunction).pipe(fourthFunction);​

PS: в качестве примера асинхронного кода я использовал setTimeout. Главное, что в конце асинхронной части нужно вызвать d.resolve() для продолжения цепочки методов.

Дополнительная литература: http://joseoncode.com/2011/09/26/a-walkthrough-jquery-deferred-and-promise/

person zerkms    schedule 05.09.2012
comment
вы должны нажать обведенную галочку под голосованием за / против, если это выбранный вами ответ. - person jeremy; 05.09.2012
comment
за исключением того, что это не совсем мой принятый ответ: P, потому что я этого не понимаю! - person tim; 05.09.2012
comment
@tim: пожалуйста, прочитайте PS. Тайм-ауты предназначены для имитации асинхронности. Замените их своей асинхронной логикой. - person zerkms; 05.09.2012
comment
потому что я этого не понимаю --- о, по крайней мере, это честно :-S Вы читали ссылку для дальнейшего чтения? - person zerkms; 05.09.2012
comment
@zerkms, мне нравится подход. Итак, вы говорите, что я готовлю каждую функцию как отложенный объект, и когда я закончу, я возвращаю ее .promise(), чтобы иметь возможность связать их вместе с .pipe() ? Я был бы очень признателен, если бы вы могли объяснить, что вы сделали немного больше. Спасибо. - person tim; 05.09.2012
comment
@tim: следующий pipe вызывается, когда предыдущий разрешается .resolve(). Вот и все. - person zerkms; 05.09.2012
comment
@zerkms, подожди минутку ... разве оператор return не будет выполнен до истечения тайм-аута? - person tim; 05.09.2012
comment
@tim: ну, я был недостаточно точен: обратный вызов канала вызывается только после разрешения предыдущего канала. Таким образом, оператор return выполняется немедленно, и у него есть объект обещания jquery, но то, что конвейерно, будет вызываться только после разрешения обещания. - person zerkms; 05.09.2012
comment
как вы это сделаете, если, скажем, вторая функция имеет параметр? firstFunction().pipe(secondFunction(param)).pipe(thirdFunction).pipe(fourthFunction);​ ? - person Nils Sens; 03.11.2016
comment
@NilsSens pipe(() => secondFunction(param)). Итак, вы передаете другую функцию, которая вызывает secondFunction с аргументом. Потому что иначе вы сразу вызываете функцию. - person zerkms; 03.11.2016
comment
Я нашел этот ответ полезным. Просто комментарий: $.pipe устарел из jQuery 1.8 в пользу $.then. - person venkrao; 24.01.2017

Идея состоит в том, что вы должны сделать что-то вроде следующего, чтобы после того, как первая функция была запущена, она знала, что запускать, в отличие от того, что вам приходилось решать это самостоятельно вне функции:

function firstFunction(callback){
  // some very time consuming asynchronous code...
  console.log('1');

  return callback(function(){
    alert("Second function finished.");
    return true;
  });
}
function secondFunction(callback){
  // waits for firstFunction to be completed
  console.log('2');

  return callback();
}

firstFunction(secondFunction);

Также посмотрите .apply() и .call().

person jeremy    schedule 05.09.2012
comment
Разве это не должна быть завершена первая функция, или я туплю? - person John Carter; 05.09.2012
comment
Это своего рода и то, и другое, но в основном это вторая функция, которая фактически завершает работу, потому что она передает функцию второй функции. Другими словами, это предупреждение будет предупреждать, когда вторая функция вызывает обратный вызов, а не когда первая функция вызывает вторую функцию. - person jeremy; 05.09.2012
comment
Вот почему я терпеть не могу эту чепуху с обратными вызовами, очень сложно отслеживать, что происходит. - person tim; 05.09.2012

Если я использую обратные вызовы, мое рабочее решение теперь выглядит так:

    one(two);
    function one(callb){
        console.log('1');
        callb(three);
    }
    function four(){
        console.log('4');
    }
    function two(callb){
        console.log('2');
        callb(four);
    }
    function three(callb){
        console.log('3');
        callb();
    }

Я нахожу это отвратительным. Как я должен отслеживать этот материал, если последовательностей больше 2-3? Вздрогнуть...

person tim    schedule 05.09.2012
comment
Как я должен отслеживать этот материал, если последовательностей больше 2-3? -- разве отсрочки не панацея? - person zerkms; 05.09.2012
comment
прочитав об этом, кажется, что это лекарство от моих болезней (мне пришлось искать панацею...) - person tim; 05.09.2012

Прошло некоторое время, и я заметил кое-что о deferreds в документации jquery, в частности о функции when ядра API.

$.when( $.ajax("test.aspx") ).then(function(ajaxArgs){ 
     alert(ajaxArgs[1]); /* ajaxArgs is [ "success", statusText, jqXHR ] */
});

Пример кода взят с сайта http://jqapi.com/#p=jQuery.when.

person tim    schedule 18.10.2012

Я играл с Promise, Sequence, Exception, Callback, чтобы понять, как это работает, и, наконец, сделал этот код.

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

function firstFunction(par) {
    return new Promise(function (resolve, reject) {
        console.log("start " + par);
        setTimeout(function (par) {
            console.log(par);
            resolve(par + 1);
        }, 1000, par);
    });
}
function secondFunction(par) {
    return new Promise(function (resolve, reject) {
        console.log("start " + par);
        setTimeout(function (par) {
            console.log(par);
            try{
                throw "Let's make an error...";
            }
            catch(err)
            {
                reject(err);
            }
            resolve(par + 1);
        }, 1000, par);
    })
}
function thirdFunction(par) {
    return new Promise(function (resolve, reject) {
        console.log("start " + par);
        setTimeout(function (par) {
            console.log(par);
            resolve(par + 1);
        }, 1000, par);
    });
}

function CatchError(error) {
    console.log("Exception: " + error);
}

//Break all chain in second function
function ChainBrake() {
    firstFunction(1)
    .then(secondFunction)
    .then(thirdFunction)
    .catch(CatchError);    
}

//Log error and continue executing chain
function ChainContinue() {
    firstFunction(1)
    .catch(CatchError)
    .then(secondFunction)
    .catch(CatchError)
    .then(thirdFunction)
    .catch(CatchError);
}
person SV0505    schedule 03.10.2016