- PVSM.RU - https://www.pvsm.ru -
В этой статье я не буду говорить о том зачем в javascript нужны промисы и в частности JQuery.Deferred. Также не буду приводить справочную информацию, ее достаточно в интернете. Например тут [1] или тут [2] или вот тут [3].
Эта статья для тек кто уже немного знаком с объектом 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));
});
Живой пример тут [4].
Запускает все асинхронные операции одновременно и переходит к выполнению следующего колбэка только тогда когда будут выполнены все параллельные операции.
$.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]));
})
Живой пример тут [5].
Задача: выполнить один запрос, а после его завершения запустить параллельное выполнение еще нескольких операций.
$.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]));
})
Живой пример [6].
Переходим к более сложным ситуациям. Допустим имеется массив со ссылками по каждой из которых необходимо сделать параллельный запрос. Задача усложняется тем, что количество ссылок в массиве заранее не известно.
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 [7].
Задача как в предыдущем примере, но запросы надо отправлять последовательно. Такой подход поможет в случае если запросов очень много, а серверная часть веб-приложения не рассчитана на такие нагрузки.
Для решения этой задачи будем “наращивать” цепочку промисов в цикле.
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 промис, который станет первым звеном цепочки.
Посмотреть [8] код в действии.
Для обработки ошибок используется метод .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');
});
Запустить [9] этот код.
Если в цепочке используется несколько обработчиков ошибок (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
Ссылка [10] для тех кто не верит.
Но скорее всего такое поведение нам не пригодится. Сделаем так чтобы после обработки ошибки ни один последующий колбэк не вызвался.
$.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');
})
Посмотреть на результат [11].
В этом примере следует обратить внимание на две вещи. Первое — обработчики ошибок теперь задаются вторым аргументом метода .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');
})
Этот [12] пример отличается от предыдущего только седьмой строкой.
Компонент JQuery.Deferred не такой сложный каким кажется при первом знакомстве с ним, впрочем такими же простыми являются остальные библиотеки реализующие функционал промисов.
Автор: format1981
Источник [13]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/javascript/65418
Ссылки в тексте:
[1] тут: http://api.jquery.com/category/deferred-object/
[2] тут: http://habrahabr.ru/post/112960/
[3] тут: https://www.google.ru/webhp?sourceid=chrome-instant&ion=1&espv=2&ie=UTF-8#newwindow=1&q=jquery+deferred
[4] тут: http://jsfiddle.net/bankrot/qTtv8/2/
[5] тут: http://jsfiddle.net/bankrot/psx39/3/
[6] Живой пример: http://jsfiddle.net/bankrot/VX9Tr/2/
[7] jsfiddle: http://jsfiddle.net/bankrot/LGuaz/2/
[8] Посмотреть: http://jsfiddle.net/bankrot/XVG5b/3/
[9] Запустить: http://jsfiddle.net/bankrot/rY8mq/2/
[10] Ссылка: http://jsfiddle.net/bankrot/LDFTF/
[11] результат: http://jsfiddle.net/bankrot/m2YVX/1/
[12] Этот: http://jsfiddle.net/bankrot/eyGh7/2/
[13] Источник: http://habrahabr.ru/post/230441/
Нажмите здесь для печати.