Вложенные асинхронные вызовы. Объект Deferred в деталях.
Объект Deferred инкапсулирует последовательность обработчиков для еще не существующего результата, чем сильно упрощает сложные AJAX-приложения. Он предоставляется различными фреймворками (Dojo Toolkit, Mochikit) и отдельными библиотечками (jsDeferred, Promises etc).
С его помощью можно добавлять обработчики не в момент запуска метода (например, посылки запроса XMLHTTPRequest() , а в любой момент после этого.
Основные методы объекта Deferred:
addCallback(функция-обработчик)
addErrback(функция-обработчик)
callback(результат)
errback(результат)
Обычно, когда функция возвращает Deferred , т.е "обещание результата в некоторый момент", программист затем цепляет к нему обработчики результата, которые будут вызваны в той же последовательности, через addCallback/addErrback.
Код, который управляет объектом Deferred , т.е тот код, который дал обещание предоставить результат, должен вызвать callback () или errback () методы в тот момент, когда результат появится. Например, после завершения некоторой асинхронной операции.
При этом будет вызван первый в цепочке обработчик, добавленный при помощи addCallback () или addErrback() соответственно.
Каждый следующий обработчик получит результат, который вернул предыдущий.
Вот - самый простой пример обработчика:
var deferred = new Deferred()
deferred.addCallback(function(result) { return result })
Важный принцип при работе с Deferred : каждый обработчик должен возвращать результат. Так что затем всегда можно продолжить работу с этим результатом.
Например, вот wrapper для XmlHTTPRequest , который возвращает объект Deferred :
function xhrGet(url) {
var deferred = new Deferred()
var xhr = new XmlHttpRequest()
xhr.open("GET", url, true);
xhr.onreadystatechange = function() {
if (xhr.readyState!=4) return
if (xhr.status==200) {
deferred.callback(xhr.responseText)
} else {
deferred.errback(xhr.statusText)
}
}
xhr.send(null)
return deferred
}
А внешний код может добавить к нему обработчики для успешно полученного результата и для ошибки:
var deferred = xhrGet("http://someurl")
deferred.addCallback(function(res) { alert("Result: "+res); return res })
deferred.addErrback(function(err) { alert("Error: "+err); return err })
Внутри Deferred - это просто упорядоченный список пар callback/errback . Методы addCallback , addErrback, addBoth и addCallbacks добавляют в него элементы.
Например, последовательность обработчиков
var deferred = new Deferred()
deferred.addCallback(myCallback)
deferred.addErrback(myErrbac)
deferred.addBoth(myBoth)
deferred.addCallbacks(myCallback, myErrback)
внутри объекта Deferred становится списком:
[
[myCallback, null],
[null, myErrback],
[myBoth, myBoth],
[myCallback, myErrback]
]
Каждый вызов add* добавляет один элемент в список, как показано выше.
Внутри у Deferred есть одно из трех состояний (свойство "fired "):
- -1, еще нет результата
- 0, есть результат "success"
- 1, произошла ошибка "error"
Deferred приходит в состояние "error" в одном из трех случаев:
- аргумент
callback или errback является instanceof Error
- из последнего обработчика выпал
exception
- последний обработчик вернул значение
instanceof Error
Во всех остальных случаях, Deferred находится в состоянии "success ".
Состояние Deferred определяет, какой обработчик будет вызван из следующего элемента последовательности. Если соответствующее значение равно null (например, надо вызвать errback , а элемент имеет вид [callback, null] ), то этот элемент пропускается.
В случае с обработчиками выше, результат будет обработан примерно так (представьте, что все exceptions перехватываются и возвращаются):
// d.callback(result) or d.errback(result)
if(!(result instanceof Error)){
result = myCallback(result);
}
if(result instanceof Error){
result = myErrback(result);
}
result = myBoth(result);
if(result instanceof Error){
result = myErrback(result);
}else{
result = myCallback(result);
}
Полученный результат затем хранится на тот случай, если к последовательности обработчиков будет добавлен новый элемент. Так как результат уже есть, то при добавлении нового обработчика - он тут же будет активирован.
Обработчики, в свою очередь, могут возвращать объекты Deferred .
При этом остальная часть цепочки исходного Deferred ждет, пока новый Deferred не вернет значение, чтобы, в зависимости от этого значения, вызвать callback или errback .
Таким способом можно сделать реализовать последовательность вложенных асинхронных вызовов.
При создании объекта Deferred можно задавать "canceller " - функцию, которая будет вызвана, если до появления результата произойдет вызов Deferred.cancel .
С помощью canceller можно реализовать "чистый" обрыв XMLHTTPRequest , и т.п.
Вызов cancel запустит последовательность обработчиков Deferred с ошибкой CancelledError (если canceller не вернет другой результат), поэтому обработчики errback должны быть готовы к такой ошибке, если, конечно, вызов cancel в принципе возможен.
Объекты Deferred , как правило, используются, чтобы сделать код асинхронным. Обычно, проще всего описать процесс в обычном, синхронном варианте, и затем разделить код, используя Deferred , чтобы отделить обработку асинхронных операций.
Например, вместо того, чтобы регистрировать callback -функцию, которая будет вызвана при окончании операции рендеринга, можно просто вернуть Deferred .
// объявление с callback
function renderLotsOfData(data, callback){
var success = false
try{
for(var x in data){
renderDataitem(data[x]);
}
success = true;
}catch(e){ }
if(callback){
callback(success);
}
}
// использование объявления с callback
renderLotsOfData(someDataObj, function(success){
// handles success or failure
if(!success){
promptUserToRecover();
}
})
Использование Deferred в данном случае не упрощает код, но задает стандартный интерфейс для задания и обслуживания любого количества обработчиков результата асинхронной операции.
Кроме того, Deferred освобождает от беспокойства на тему "а, может, вызов уже произошел?", например, в случае возврата результата из кеша. С Deferred , новые обработчики могут быть добавлены в любой момент, даже если результат уже получен.
function renderLotsOfData(data){
var d = new Deferred();
try{
for(var x in data){
renderDataitem(data[x]);
}
d.callback(true);
}catch(e){
d.errback(new Error("rendering failed"));
}
return d;
}
// использование Deferred
renderLotsOfData(someDataObj).addErrback(function(){
promptUserToRecover();
});
// NOTE: addErrback и addCallback возвращают тот же Deferred,
// так что мы можем тут же добавить в цепочку новые обработчики
// или сохранить deferred для добавления обработчиков позже.
В этом примере renderLotsOfData работает синхронно, так что оба варианта довольно-таки искусственные. Поставим отображение данных в setTimeout (типа идет анимация), чтобы почувствовать, как крут Deferred :
// Deferred и асинхронная функция
function renderLotsOfData(data){
var d = new Deferred()
setTimeout(function(){
try{
for(var x in data){
renderDataitem(data[x]);
}
d.callback(true);
}catch(e){
d.errback(new Error("rendering failed"));
}
}, 100);
return d;
}
// используем Deferred для вызова
renderLotsOfData(someDataObj).addErrback(function(){
promptUserToRecover()
})
// Заметим, что вызывающий код не потребовалось исправлять
// для поддержки асинхронной работы
Благодаря Deferred - почти не пришлось ничего менять, порядок обработчиков соответствует реально происходящему, в общем - все максимально удобно и наглядно!
Объект DeferredList расширяет Deferred .
Он позволяет ставить каллбек сразу на пачку асинхронных вызовов.
Это полезно, например, для асинхронной загрузки всех узлов на одном уровне javascript-дерева, когда хочется сделать что-то после окончания общей загрузки.
Объект Deferred есть в javascript-фреймворках Dojo и Mochikit. Кроме того, и там и там есть вспомогательный объект DeferredList .
Собственно, эта статья написана частично по документации dojo. Автор считает, что имеет на это право, т.к сам приложил руку к этой части разработки данного фреймворка . Впрочем, из обоих фреймворков Deferred можно легко вынуть и приспособить к собственной библиотеке, если таковая у вас имеется.
Успешной асинхронной разработки.
|
Что-то я дочитал до конца и понял, что Deffered только при наличии фреймворков есть. Можно было это в начале написать))
> С помощью canceller можно реализовать "чистый" обрыв XMLHTTPRequest, и т.п.
что такое "чистый обрыв XHR"?
не совсем понятно зачем нужен canceler, можете привести пример?
Для отмены любого асинхронного события.
Часто необходимо для работы с вводом в Text-box'ы - когда человек нажал 1ую букву пошёл первый запрос, через несколько миллисекунд человек нажал вторую букву - пошёл второй запрос. Если, например на сервере идёт select с условием where text like '<введённые символы>%' то результат с 2ми буквами может вернуть гораздо меньше строк (в разы) да и выполнится быстрее, скорее всего - в результате получается, что ответ от запроса 2 может придти раньше 1-го, а первый, в свою очередь, может "затереть" второй. Для пользователя это будет выглядеть, так, как будто 2ой запрос не выполнился.
если я правильно понимаю, то мы тут сначала вызываем функцию, а потом добавляем колбэки? Но при асинхронном вызове может получиться, что результат будет получен раньше, чем будет добавлен колбэк.
В таком случае callback будет вызван сразу же после его добавления к объекту Deferred.
Если у Deferred вызвать errback с "не ошибкой", например .errback(10) - по описанию получается что Deferred будет в состоянии "success" и вызовы начнутся с callback- в не errback-функций.
Так ли это? Правильно ли это?
С простыми Deferred всё просто и понятно. Но проблемы начинаются когда начинается вложенность и циклы.
К примеру, есть массив id юзеров
Нужно к примеру, асинхронно вернуть названия групп в которых состоят эти юзеры. (Пример придуман из головы чтобы показать проблему).
предположим, есть в наличии два XMLHttpRequest : getGroupIdByUserId и getGroupNameByGroupId
Просто, не правда ли? Но попробуйте написать код, который бы это делал асинхронно, используя Deferred и остался читабельным.
console.log('hello')
Можно конечно использовать существующие библиотеки, но мне нравится этот вариант. happy wheels free play
Объект Deferred инкапсулирует последовательность обработчиков для еще не существующего результата, чем сильно упрощает сложные AJAX-приложения. Он предоставляется различными фреймворками (Dojo Toolkit, Mochikit) и отдельными библиотечками (madalin stunt cars 2, Promises etc).
Thanks for this wonderful article and continue sharing more topics like this.
cool math games
Thanks for this all detailed information. Providing all the coding and coding errors and telling us the proper way of doing it, happy wheels unblocked free online.
Thanks for sharing this wonderful article.
In stressful working hours, your work is too stressful. We always create a sense of comfort for you so you have a relaxing time. You try dinosaur game an extremely fun game. thank you!
This is such a great resource that you are providing and you give it away for free.
This is such a great resource that you are providing and you give it away for free.
java is best useable programming language if you want to learn how to reinstall onedrive just visit windowsclassroom website and learan about OneDrive
Why it is so hard to understand coding process. Thanks for sharing this geometry dash unblocked
My friend's blog I read, I am very impressed with your blog, I hope you will have more blogs or more posts to bring to readers.
vex 3
Excellent post. Please keep up the great work. You may check our website also Visit: free fonts
offshore hosting with 100% DMCA ignored Hosting, Offshore Dedicated Server, Offshore VPS Hosting. offshorededi.com is the Most Secure Offshore Hosting. Providing Offshore Streaming Servers as well.
Hi there, I found your website via Google while searching for a related topic, your website came up, it looks great. I have bookmarked it in my google bookmarks.
Мне нужно работать с этим кодированием, я могу полностью понять, возможно, сделав несколько попыток, спасибо за предоставленную информацию.
bubbles
Хотя я пытаюсь работать с этими кодировками, иногда я не могу понять, и это заставляет меня очень стараться. basketball legends
You provided a lot of good information, it was good because it was so helpful to me. word finder
Oh, great, your article provided me with useful information and a fresh perspective on the subject. Check to finish your trip!
Your texts on this subject are correct, see how I wrote this site is really very good 먹튀검증커뮤니티
Your texts on this subject are correct, see how I wrote this site is really very good 소액대출
do you like that
word counter tool
Хотя мы можем исправить ошибку, вызвав src.Close () перед оператором return во втором предложении return; Но по мере того, как код становится более сложным, подобные проблемы становится все труднее найти и 2 player games решить. Мы можем использовать предложение defer, чтобы гарантировать, что нормально открытый файл также будет закрыт.
Отправить комментарий
Приветствуются комментарии:Для остальных вопросов и обсуждений есть форум.