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

    alexey-m-ukolov
    @alexey-m-ukolov Куратор тега JavaScript
    Дисклеймер
    Кому не нравится название "обещания", мысленно заменяйте его на то, которое считаете подходящим. Я выбрал именно его, чтобы концепция, лежащая в их основе, была интуитивно понятна.

    Если функция асинхронная, то лучше всего использовать обещания, что вы и попытались сделать (интерактивный пример).
    one().done(two);
    
    function one() {
        var dfd = new $.Deferred();
    
        // Запускаем асинхронную задачу. Например, ajax-запрос.
        setTimeout(function () {
            var foo = 'bar';
    
            // "Выполняем обещание", передавая в него какую-то информацию.
            // Передавать аргументы, разумеется, не обязательно.
            dfd.resolve(foo);
        }, 2000);
    
        // Возвращаем из функции обещание, на которое могут подписаться другие функции.
        // Обратите внимание, этот код выполнится до того, как завершится асинхронная задача.
        return dfd.promise();
    }
    
    function two(foo) {
        // Обрабатываем данные, полученные внутри асинхронной функции one.
        console.log('two', foo);
    }

    Для трех функций расклад немного сложнее, но принцип такой же.
    Есть и более элегантный способ запуска цепочки из трех функций:
    код
    one().then(two, onOneError).then(three, onTwoError);
    
    function one() {
        var dfd = new $.Deferred();
    
        setTimeout(function () {
            console.log('one');
            
            if (Math.round(Math.random() * 10) >= 5)
            {
                dfd.resolve();
            }
            else
            {
                dfd.reject();
            }
        }, 1000);
    
        return dfd.promise();
    }
    
    function two() {
        var dfd = new $.Deferred();
    
        setTimeout(function () {
            console.log('two');
            
            if (Math.round(Math.random() * 10) >= 5)
            {
                dfd.resolve();
            }
            else
            {
                dfd.reject();
            }
        }, 1000);
    
        return dfd.promise();
    }
    
    function three() {
        setTimeout(function () {
            console.log('three');
        }, 1000);
    }
    
    function onTwoError() {
        console.log('twoError', arguments);
    }
    
    function onOneError() {
        console.log('oneError', arguments);
    }

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


    Обещания - самый современный и удобный вариант, с ними код становится чище и понятнее. Единственная проблема, связанная с ними - это необходимость использовать сторонние библиотеки или полифилы, потому что обещания пока поддерживаются далеко не во всех браузерах.

    Другой вариант - передавать callback, но это прямой путь в callback hell. Для запуска трех и более функций подряд я его не рекомендую - смотрите сами, на что становится похож код:
    one(function () {
        two(three)
    });
    
    function one(callback) {
        console.log('one');
        setTimeout(callback, 1000);
    }
    
    function two(callback) {
        console.log('two');
        setTimeout(callback, 1000);
    }
    
    function three() {
        console.log('three');
    }


    Есть еще один (очень, очень, очень плохой) вариант, основанный на таймерах и внешних флагах. Никогда так не делайте (код для системы из трех функций еще хуже).

    Если внутри функций ничего асинхронного нет, то можно просто вызвать их друг за другом - следующая и так запустится после предыдущей (пример).
    Ответ написан
    5 комментариев