Прокачиваем AJAX
У нас есть три способа для «прокачки» AJAX в jQuery: это создание префильтров, добавление новых конверторов и транспортов.
Префильтры
Префильтр – это функция, которая будет вызвана до «ajaxStart», в ней вы сможете изменить как объект «jqXHR», так и любые сопутствующие настройки:
// регистрация AJAX префильтра
$.ajaxPrefilter(function( options, originalOptions, jqXHR ) {
// наши манипуляции над настройками и jqXHR
});
Для чего всё это? Да вот простая задачка – не ждать «старый» AJAX-ответ, если мы запрашиваем URL заново:
// коллекция текущих запросов
var currentRequests = {};
$.ajaxPrefilter(function( options, originalOptions, jqXHR ) {
// наша произвольная настройка
if ( options.abortOnRetry ) {
if ( currentRequests[ options.url ] ) {
// отменяем старый запрос
currentRequests[ options.url ].abort();
}
currentRequests[ options.url ] = jqXHR;
}
});
// вызов с использованием фильтра
$.ajax({
/* ... */
abortOnRetry: true
})
Ещё можно изменить опции вызова, вот пример, который по флагу «crossDomain» пересылает запрос на заранее подготовленную проксирующую страницу на нашем сервере:
$.ajaxPrefilter(function( options ) {
if ( options.crossDomain ) {
options.url = "/proxy/" + encodeURIComponent( options.url );
options.crossDomain = false;
}
});
Префильтры можно «вешать» на определенный тип «dataType» (т.е. в зависимости от ожидаемого типа данных от сервера будут срабатывать различные фильтры):
$.ajaxPrefilter("json script", function(options, original, jqXHR) {
/* ... */
});
Ну и последнее: для переключения «dataType» на какой-нибудь другой тип нам достаточно будет вернуть необходимое значение:
$.ajaxPrefilter(function( options ) {
// это наша функция-детектор необходимых URL
if ( isActuallyScript( options.url ) ) {
// теперь «ждём» script
return "script";
}
});
Будьте очень осторожны, когда оперируете глобальными настройками, да ещё через такую неявную фичу, как фильтры. Задокументируйте подобные подходы в сопроводительной документации, иначе разработчики, которые будут в дальнейшем сопровождать ваш код, будут сильно ругаться (в качестве оных через пару месяцев можете оказаться и вы сами). И в целом настоятельно рекомендуется достаточно подробное документирование и комментирование кода. Меткая фраза, ошибочно приписываемая мэтру программирования МакКоннеллу: "Пишите код так, как будто сопровождать его будет склонный к насилию психопат, который знает, где вы живёте" (Джон Ф. Вудс).
Конверторы
Конвертор – функция обратного вызова, которая вызывается в том случае, когда полученный тип данных не совпадает с ожидаемым (т.е. «dataType» указан неверно).
Все конверторы хранятся в глобальных настройках «ajaxSettings»:
// формат ключа "из_формата в_формат"
// в качестве входного формата можно использовать "*"
{
converters: {
"* text": window.String, // что угодно приводим к тексту
"text html": true, // текст к HTML (флаг true == без изменений)
"text json": jQuery.parseJSON, // текст к JSON
"text xml": jQuery.parseXML // разбираем текст как XML
}
}
Для расширения набора конверторов потребуется функция «$.ajaxSetup()»:
$.ajaxSetup({
converters: {
"text mydatatype": function( textValue ) {
if ( valid( textValue ) ) {
// разбор пришедших данных
return mydatatypeValue;
} else {
// возникла ошибка
throw exceptionObject;
}
}
}
});
Имена «dataType» должны всегда быть в нижнем регистре.
Конверторы следует использовать, если требуется внедрить произвольные форматы «dataType», или для конвертации данных в нужный формат. Необходимый «dataType» указываем при вызове метода «$.ajax()»:
$.ajax( url, { dataType: "mydatatype" });
Конверторы можно задавать также непосредственно при вызове «$.ajax()», дабы не засорять общие настройки:
$.ajax( url, {
dataType: "xml text mydatatype",
converters: {
"xml text": function( xmlValue ) {
// получаем необходимые данные из XML
return textValue;
}
}
});
Чуть-чуть пояснений: мы запрашиваем «XML», который конвертируем в текст, который будет передан в наш конвертор из «text» в «mydatatype».
Транспорт
Использование своего транспорта – это крайняя мера, прибегайте к ней только в том случае, если с поставленной задачей нельзя справиться с использованием префильтров и конверторов.
Транспорт – это объект, который предоставляет два метода «send()» и «abort()» – они будут использоваться внутри метода «$.ajax()». Для регистрации своего метода транспортировки следует использовать метод «$.ajaxTransport()», будет это выглядеть как-то так:
$.ajaxTransport( function( options, originalOptions, jqXHR ) {
if ( /* transportCanHandleRequest */ ) {
return {
send: function( headers, completeCallback ) {
/* отправляем запрос */
},
abort: function() {
/* отменяем запрос */
}
};
}
});
Проясню чуток параметры, с которыми будем работать:
options
– настройки запроса (то, что указываем при вызове «$.ajax()»)
originalOptions
– «чистые» настройки, даже без учёта изменений «по умолчанию»
jqXHR
– объект «jQuery XMLHttpRequest»
headers
– заголовки запроса в виде связки «ключ-значение»
completeCallback
– функция обратного вызова, её следует использовать для оповещения о завершении запроса
Функция «completeCallback()» имеет следующую сигнатуру:
function ( status, statusText, responses, headers ) {
/* какой-то код */
}
где:
status
– HTTP статус ответа
statusText
– текстовая интерпретация ответа
responses
– опционально – объект, содержащий ответы сервера во всех форматах, которые поддерживает транспорт; для примера, родной «XMLHttpRequest» будет выглядеть как «{ xml: XMLData, text: textData }» при запросе XML документа
headers
– опционально – строка, содержащая заголовки ответа сервера, если транспорт может их получить; например, метод «XMLHttpRequest.getAllResponseHeaders()» это осилит
Как и префильтры, транспорт можно привязывать к определенному типу запрашиваемых данных:
$.ajaxTransport( "script", function( options, originalOptions, jqXHR ) {
/* привязываемся лишь к script*/
});
А теперь мега-напряг – добавим транспорт «image» на данную страницу:
// регистрируем его для типа image
$.ajaxTransport("image", function (options) {
// отслеживаем лишь GET AJAX-запросы
if (options.type === "GET" && options.async) {
var image;
return {
//
send:function (_, callback) {
image = new Image();
// подготовим функцию done
function done(status) {
if (image) {
var statusText = ( status == 200 ) ? "success" : "error", tmp = image;
image = image.onreadystatechange = image.onerror = image.onload = null;
callback(status, statusText, { image:tmp });
}
}
image.onreadystatechange = image.onload = function () {
done(200);
};
image.onerror = function () {
done(404);
};
image.src = options.url;
},
abort:function () {
if (image) {
image = image.onreadystatechange = image.onerror = image.onload = null;
}
}
};
}
});
Теперь можно воспользоваться данным транспортом:
$.ajax('../code/img/photo-cat.jpg', {
dataType:'image',
success: function(data) {
$('#image').html(data);
}
})
Я хотел бы ещё раз напомнить, что это «advanced level», и данный раздел лишний в учебнике «для начинающих».
По следам официальной документации: