Pull to refresh

JQuery Deferred — примеры использования

Bankrot-pro corporate blogJavaScriptjQuery
В этой статье я не буду говорить о том зачем в javascript нужны промисы и в частности JQuery.Deferred. Также не буду приводить справочную информацию, ее достаточно в интернете. Например тут или тут или вот тут.
Эта статья для тек кто уже немного знаком с объектом Deferred из библиотеки JQuery, но не имеет опыта написания сложных цепочек (очередей).

Подготовка

Все примеры кода, рассмотренные здесь, используют асинхронный метод $.ajax(), который возвращает так называемый jqXHR в котором реализованы промис-методы (done, fail, always, then). Нам нужны будут только они, поэтому будем считать что $.ajax возвращает промис (promise).
В некоторых примерах используются методы $.map() и $.each(), которые входят в состав библиотеки JQuery.

Последовательное выполнение

Простейшее использование промисов — это последовательное выполнение асинхронных операций. То есть следующая операция не начинается пока текущая не закончится.

$.ajax('http://echo.jsontest.com/id/1')
.then(function(result){
    console.log(JSON.stringify(result));
    return $.ajax('http://echo.jsontest.com/id/2')
}).then(function(result){
    console.log(JSON.stringify(result));
    return $.ajax('http://echo.jsontest.com/id/3')
}).then(function(result){
    console.log(JSON.stringify(result));
});

Живой пример тут.

Параллельное выполнение

Запускает все асинхронные операции одновременно и переходит к выполнению следующего колбэка только тогда когда будут выполнены все параллельные операции.

$.when(
    $.ajax('http://echo.jsontest.com/id/1'),
    $.ajax('http://echo.jsontest.com/id/2'),
    $.ajax('http://echo.jsontest.com/id/3')
).then(function(result1, result2, result3){
    console.log(JSON.stringify(result1[0]));
    console.log(JSON.stringify(result2[0]));
    console.log(JSON.stringify(result3[0]));
})

Живой пример тут.

Последовательно-параллельное выполнение

Задача: выполнить один запрос, а после его завершения запустить параллельное выполнение еще нескольких операций.

$.ajax('http://echo.jsontest.com/id/1')
.then(function(result1){
    console.log(JSON.stringify(result1));
    return $.when(
        $.ajax('http://echo.jsontest.com/id/2'),
        $.ajax('http://echo.jsontest.com/id/3'),
        $.ajax('http://echo.jsontest.com/id/4')
    )
}).then(function(result2, result3, result4){
    console.log(JSON.stringify(result2[0]));
    console.log(JSON.stringify(result3[0]));
    console.log(JSON.stringify(result4[0]));
})

Живой пример.

Параллельное выполнение неизвестного количества асинхронных операций

Переходим к более сложным ситуациям. Допустим имеется массив со ссылками по каждой из которых необходимо сделать параллельный запрос. Задача усложняется тем, что количество ссылок в массиве заранее не известно.

array = ['/url1', '/url2', ….. '/urlN']

Мы уже знаем, что для параллельного запуска используется метод $.when следующим образом:

$.when(promise1, promise2, … promiseN)

Но, к сожалению, нельзя передать в этот метод массив промисов. Поэтому придется этот код можно немного переписать, и тогда он подойдет для нашей задачи:

$.when.apply(this, [promise1, promise2, … promiseN])

А вот полностью решение задачи:

urls = ['http://echo.jsontest.com/id/1', 'http://echo.jsontest.com/id/2', 'http://echo.jsontest.com/id/3']
promises = $.map(urls, function(url){
    return $.ajax(url).then(function(result){
        console.log(JSON.stringify(result));
    });
});
$.when.apply(this, promises)
.then(function(){
    console.log('done');
});

Этот же код на jsfiddle.

Последовательное выполнение неизвестного количества асинхронных операций

Задача как в предыдущем примере, но запросы надо отправлять последовательно. Такой подход поможет в случае если запросов очень много, а серверная часть веб-приложения не рассчитана на такие нагрузки.
Для решения этой задачи будем “наращивать” цепочку промисов в цикле.

urls = ['http://echo.jsontest.com/id/1', 'http://echo.jsontest.com/id/2', 'http://echo.jsontest.com/id/3']
promise = $.when();
$.each(urls, function(index, url){
    promise = promise.then(function(){
        return $.ajax(url);
    }).then(function(result){
        console.log(JSON.stringify(result));
    });
});
promise.then(function(){
    console.log('OK');
});

Здесь я применил небольшой трюк: promise = $.when(). Запуск метода $.when без аргументов вернет resolved промис, который станет первым звеном цепочки.
Посмотреть код в действии.

Простая обработка ошибок: один обработчик для всех операций

Для обработки ошибок используется метод .fail. В примере ниже этот метод находится в самом конце цепочки промисов и при возникновении ошибки все done-колбэки пропускаются.

$.ajax('http://echo.jsontest.com/id/1')
.then(function(){
    console.log('OK 1');
    return $.ajax('http://echo.jsontest.com/id/2');
}).then(function(){
    console.log('OK 2');
    return $.ajax('http://echo.jsontest_fail.com/id/3');
}).then(function(){
    console.log('OK 3');
    return $.ajax('http://echo.jsontest.com/id/4');
}).then(function(){
    console.log('OK 4');
}).fail(function(){
    console.log('error');
});

Запустить этот код.

Остановка выполнения цепочки после обработки ошибки

Если в цепочке используется несколько обработчиков ошибок (rejected промисов), то при возникновении ошибки будут вызваны все последующие fail-колбэки. Пример:

$.ajax('http://echo.jsontest.com/id/1')
.then(function(){
    console.log('OK 1');
    return $.ajax('http://echo.jsontest.com/id/2');
}).then(function(){
    console.log('OK 2');
    return $.ajax('http://echo.jsontest_fail.com/id/3');
}).fail(function(){
    console.log('error 1');
}).then(function(){
    console.log('OK 3');
    return $.ajax('http://echo.jsontest.com/id/4');
}).fail(function(){
    console.log('error 2');
}).then(function(){
    console.log('OK 4');
}).fail(function(){
    console.log('error 3');
});


После выполнения этого кода увидим в консоли следующее:

OK 1
OK 2
error 1
error 2
error 3

Ссылка для тех кто не верит.
Но скорее всего такое поведение нам не пригодится. Сделаем так чтобы после обработки ошибки ни один последующий колбэк не вызвался.

$.ajax('http://echo.jsontest_fail.com/id/1')
.then(function(){
    console.log('OK 1');
    return $.ajax('http://echo.jsontest.com/id/2');
}, function(){
    console.log('error 1');
    return $.Deferred();
}).then(function(){
    console.log('OK 2');
}, function(){
    console.log('error 2');
})

Посмотреть на результат.
В этом примере следует обратить внимание на две вещи. Первое — обработчики ошибок теперь задаются вторым аргументом метода .then. Второе — обработчик ошибок возвращает Deferred объект (промис) который не является ни resolved ни rejected.

Продолжение выполнения цепочки после обработки ошибки

Немного изменив предыдущий пример можно сделать так что после обработки ошибки будет вызван следующий done-колбэк.

$.ajax('http://echo.jsontest_fail.com/id/1')
.then(function(){
    console.log('OK 1');
    return $.ajax('http://echo.jsontest.com/id/2');
}, function(){
    console.log('error 1');
    return $.when();
}).then(function(){
    console.log('OK 2');
}, function(){
    console.log('error 2');
})

Этот пример отличается от предыдущего только седьмой строкой.

Заключение

Компонент JQuery.Deferred не такой сложный каким кажется при первом знакомстве с ним, впрочем такими же простыми являются остальные библиотеки реализующие функционал промисов.

UPD

Спасибо пользователю mayorovp за комментарий
Tags:JQueryDeferred
Hubs: Bankrot-pro corporate blog JavaScript jQuery
Total votes 54: ↑45 and ↓9+36
Views64K

Information

Founded
Location
Россия
Website
bankrot-pro.com
Employees
1 employee (me only)
Registered