Объект Deferred.
	
    
        
        
	
	
            
        
	
Каждый, кто когда-либо использовал AJAX, знаком с асинхронным программированием. Это когда мы запускаем некий процесс (скажем, XMLHTTPRequest) и задаем функцию callback обработки результата. 
На первый взгляд, все очень просто, но лишь до тех пор, пока мы не захотим добавить вызов новой функции после callback, сделать удобную обработку ошибок и исключений, а также - предусмотреть добавление новых функций в конец цепочки асинхронных вызовов. 
Один способ - добавлять каллбэки в параметры всех функций. Другой - использовать для управления асинхронностью отдельный объект. Назовем его Deferred. 
Такой объект есть, например, в библиотеке Mochikit и во фреймворке dojo. 
Начнем - издалека, с простого примера, который прояснит суть происходящего. Если кому-то простые примеры не нужны  - следующий раздел: Асинхронное программирование не для чайников. <code>Deferred</code>.. 
Нужно отправить результат голосования на сервер и показать ответное сообщение. 
Посылкой данных на сервер занимается функция sendData. Эта функция - общего вида, вызывается в куче мест. 
В зависимости от результата вызывается callback, если все ок, либо errback - в случае ошибки при запросе. 
function sendData(url, data, callback, errback) {
	var xhr = new XmlHttpRequest()
	xhr.onreadystatechange = function() {
		if (xhr.readyState==4) {
			if (xhr.status==200) {
				callback(xhr.responseText)  
			} else {
				errback(xhr.statusText)
			}
		}			
	}
	xhr.open("POST", url, true); 
	xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded')
	xhr.send("data="+encodeURIComponent(data))
}
Функция processVoteResult обрабатывает JSON-результат ответа сервера. 
function processResult(data) {
	var data = eval( '('+data+')' )
	showMessage(data.text)  
}
Обработку ошибок пусть делает функция handleError, для простоты: 
function handleError(error) {  alert(error)  }
Используя функцию отправки sendData, обработки результата processResult, теперь можно записать, наконец, метод голосования: 
function vote(id) {
	sendData("http://site.ru/vote.php", id, processResult, handleError)
}
В простейшем случае, все - работа закончена. Но полученная реализация обладает рядом недостатков. 
- Не учитывается возможность ошибки в processResult, например, при вызове eval.
 
- При использовании этого кода нельзя добавить в цепочку вызовов новую функцию 
updateVoteInfo(), чтобы, она, скажем, сработала после showMessage. 
 
Один способ решения проблемы 2 - это добавить к processResult аргумент callback и вставить вызов updateVoteInfo через промежуточный обработчик результата в функции vote: 
function sendData(url, data, callback) {
	var xhr = new XmlHttpRequest()
	xhr.onreadystatechange = function() {
	    if (xhr.readyState==4) { callback(xhr.responseText)  }
	}
	xhr.open("POST", url, true); 
	xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded')
	xhr.send("data="+encodeURIComponent(data))
}
function processResult(data, callback) {
	var data = eval( '('+data+')' )
       	showMessage(data.text)         
	callback(data)
}
function vote(id) {	
	var process = function(result) {
		processResult(result, updateVoteInfo)
	}
		
	sendData("http://site.ru/vote.php", id, process, handleError)
}
Надеюсь, в коде все понятно, т.к пока что он довольно простой... Но как сделать так, чтобы исключение внутри любого асинхронного обработчика (например, processResult) не приводило к ошибке javascript и падению всей цепочки? 
Чтобы обработка ошибок и исключения была схожей - добавим к обработчику аргумент errback и будем вызывать его при исключении. А заодно сделаем то же самое и с функцией vote. 
function sendData(url, data, callback, errback) {
	var xhr = new XmlHttpRequest()
	xhr.onreadystatechange = function() {
		if (xhr.readyState==4) {
			if (xhr.status==200) {
				callback(xhr.responseText)  
			} else {
				errback(xhr.statusText)
			}
		}			
	}
	xhr.open("POST", url, true); 
	xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded')
	xhr.send("data="+encodeURIComponent(data))
}
function processResult(data, callback, errback) {
	try {
		var data = eval( '('+data+')' )
		showMessage(data.text)         
		callback(data)
	} catch(e) {
		errback(e.message)
	}                             
}
function vote(id, callback, errback) {
	var process = function(result) {
		processResult(result, callback, errback)
		updateVoteInfo(result)
	}
		
	sendData("http://site.ru/vote.php", id, process, errback)
}
Код получился чуть переусложненный. Сравним с тем же, но синхронным кодом: 
function vote(id) {
	try {
		var result = sendData("http://site.ru/vote.php", id)
		processResult(result)
		updateVoteInfo(result)
	} catch(e) {
		handleError(e)
	}
}
function processResult(data) {
	var data = eval( '('+data+')' )
	showMessage(data.text)
}
function sendData(url, data, callback) {
	var xhr = new XmlHttpRequest()
	xhr.open("POST", url, false); 
	xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded')
	xhr.send("data="+encodeURIComponent(data))
	if (xhr.status==200) {
		return xhr.responseText	
	} else {
		throw xhr.statusText
	}
}
- В асинхронном коде аргументы 
callback/errback
- их нет при обычном, последовательном программировании
 
 
 
- В асинхронном коде обратная последовательность действий
- сначала делается обработчик результата, а потом - вызывается метод
 
 
 
- Асинхронный код длиннее
 
 
Аналогичный пример с объектом Deferred выглядел бы так 
function vote(id) {
	var deferred = sendData("http://site.ru/vote.php", id)
	deferred.addCallback(processResult)
	deferred.addCallback(updateVoteInfo)
	deferred.addErrback(handleError)
}
function sendData(url, data) {	
	var xhr = new XmlHttpRequest()
	xhr.open("POST", url, true); 
	xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded')
	var deferred = new Deferred()
	xhr.onreadystatechange = function() {
		if (xhr.readyState==4) {
			if (xhr.status==200) {
				deferred.callback(xhr.responseText)  
			} else {
				deferred.errback(xhr.statusText)
			}
		}			
	}
	xhr.send("data="+encodeURIComponent(data))
	return deferred
}
function processResult(data) {
	var data = eval( '('+data+')' )
	showMessage(data.text)         
	return data
}
Смысл объекта Deferred - состоит в исключении всей асинхронности из кода. Асинхронностью занимается объект Deferred. 
А сам код становится проще, понятнее и позволяет легко организовывать цепочки асинхронных вызовов. 
Следующая статья описывает сам объект Deferred в деталях. 
	
	 | 
Имеет смысл написать, что в реализации JavaScript нет встроенного конструктора Deferred, и для использования этого стека обработчиков нужно подключить фреймворк. Иначе это с ходу вводит в заблуждение.
А можно увидеть внутреннюю реализацию Deferred? Уверен, что она не такая сложная, чтобы создавать отдельный класс для решения такой задачи.
Вы сами себе противоречите: навешиваете обработчик на onreadystatechange ПОСЛЕ действия (open, send).
интересный подход, но я немного не понял следующее:
var deferred = sendData("http://site.ru/vote.php", id) deferred.addCallback(processResult) deferred.addCallback(updateVoteInfo) deferred.addErrback(handleError)сперва делается sendData, а только потом addCallback/addErrback, понятно дело что при честном http-запросе, код выполнится раньше чем придет результат, но если говорить вообще об асинхронном программировании, результат может прийти раньше чем добавятся обработчики. Или может я что-то не так понял?
Ну, теоретически результат может прилететь раньше, но на практике вряд-ли. К тому же, идеологически, в навешивании callback-а не должно быть никакой сложной логики, и тот код должен выполнятся сравнительно моментально.
Дополнение - в самом Deffered должно быть предусмотрено, что если методом addCallback(callback) обработчик добавляется после того, как событие произошло, то callback вызывается сразу-же.
Не просто "должно быть предусмотрено" -- оно там в самом деле предусмотрено.
если ответ придёт раньше, чем будет навешен обработчик - то обработчик запустится немедленно.
Таким образом deferred избавляет нас от кошмарной вложенности callback'ов, код выглядит "последовательным" и более читабельным
Такие допущения приводят к "плавающим" ошибкам, которые отловить практически нельзя.
Что касается "в теории": если файл закеширован, а при навешивании все-таки происходят некоторые действия, ответ можно получить раньше чем ожидается.
Что касается "на практике": при отладке поставь точку останова на
и все обработчики пойдут лесом. По моему весьма жизненно.
Но подход интересный. В больших проектах асинхронные баги добавляют немало головной боли.
нет не должен, вы не учитивает работу событийного цикла.
Почему бы не написать проще, без всяких deferred:
var xhr = new XmlHttpRequest() function sendData(url, data) { xhr.onreadystatechange = send_Handler; xhr.open("POST", url, true); xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded') xhr.send("data="+encodeURIComponent(data)) } function send_Handler() { if (xhr.readyState==4) { if (xhr.status==200) processResult(xhr.responseText); else handleError(xhr.statusText); } } function processResult(data) { var data = eval( '('+data+')' ) showMessage(data.text) } function handleError(error) { alert(error) }