Создание скрытого фрейма IFRAME вручную

В заключение рассмотрим ситуацию, когда в странице необходимо создать скрытый плавающий фрейм IFRAME, чтобы загружать в него некоторое содержимое, и необходимо реализовать передачу извещения, когда содержимое будет загружено. В отличие от функции dojo.io.iframe.send, которая создает элемент IFRAME и тут же посылает запрос на получение содержимого, функция dojo.io.iframe.create создает элемент IFRAME и позволяет определить фрагмент программного кода JavaScript, который будет выполнен после того, как создание и наполнение фрейма завершится. Эта функция имеет следующую сигнатуру:

dojo.io.iframe.create(/*String*/frameName, /*String*/onLoadString,
/*String?*/url)//Возвращает DOMNode


В сущности, вы должны определить имя фрейма, строковое значение, которое будет рассматриваться как функция обратного вызова, и не обязательный адрес URL для фрейма. Ниже приводится пример, который загружает содержимое адреса URL в скрытый плавающий фрейм IFRAME на странице и по окончании загрузки вызывает функцию обрат
ного вызова:
<html>
<head>
<title>Fun with IFRAME Transports!</title>
<script type="text/javascript"
src="http://o.aolcdn.com/dojo/1./dojo/dojo.xd.js"
djConfig="isDebug:true,dojoBlankHtmlUrl:'/path/to/blank.html'"
</script>
<script type="text/javascript">
dojo.require("dojo.io.iframe");
function customCallback() {
console.log("callback!");
//здесь можно сослаться на фрейм с помощью
//dojo.byId("fooFrame")...
}
create = function() {
dojo.io.iframe.create("fooFrame", "customCallback()",
"http://www.exmaple.com");
}
</script>
</head>
(body)
(button onclick="javascript:create();")Create(/button)
(/body)
</html>


Чаще всего вы будете немедленно загружать содержимое в элемент IFRAME, но иногда бывает необходимо просто создать пустой фрейм. Если вы пользуетесь версией инструментального набора для локальной работы, то для создания пустого фрейма достаточно всего лишь опустить третий параметр в вызове функции dojo.io.iframe.create. Если же вы пользуетесь версией XDomain, то вам необходимо будет указать путь к локальному шаблону, представляющему содержимое. В каталоге с инструментальным набором этот шаблон находится в файле dojo/resources/blank.html, который можно скопировать в удобное для вас местоположение. Кроме того, прежде чем создавать IFRAME, в массив djConfig необходимо добавить дополнительный параметр настройки, как показано в примере следующего раздела.

Добавлено: 24 Июля 2018 20:52:20 Добавил: Андрей Ковальчук

Типы ответов, отличные от HTML

В предыдущем примере сервер возвращал в качестве ответа документ HTML, который можно было извлечь и выполнить над ним некоторые манипуляции. Однако, когда ответ не является разметкой HTML, необходимо соблюсти специальное условие – текст ответа должен заключаться в тег textarea. Как оказывается, использование документа HTML является единственно надежным и переносимым способом передачи данных с помощью IFRAME, а тег textarea является естественным средством транспортировки содержимого в виде текста. Конечно, внутренние механизмы Dojo извлекают это содержимое и используют его как тело ответа. В следующем примере показано, как следует изменить предыдущий пример, чтобы в качестве ответа можно было использовать простой текст, а не разметку HTML.

В сценарии CherryPy нужно только добавить обслуживание файла foo.html и окончательный ответ обернуть в тег textarea, как показано ниже:

import cherrypy
import os
# программный код, выполняющий запросы XHR, будет находиться в файле foo.html,
# и это видно в следующей директиве config
current_dir = os.getcwd()
config = {'/' :
{
'tools.staticfile.on' : True,
'tools.staticfile.filename' : os.path.join(current_dir, 'foo.html')
}
}
local_file_path="/tmp/uploaded_file"
class Content:
#обслуживает передачу файла...
@cherrypy.expose
def upload(self, inbound):
outfile = open(local_file_path, 'wb')
inbound.file.seek(0)
while True:
data = inbound.file.read(8192)
if not data:
break
outfile.write(data)
outfile.close()
return "<html><head></head><textarea>Thanks!</textarea></
body></html>"
cherrypy.quickstart(Content(), '/', config=config)


Единственное, что следует изменить в самом запросе, – это указать иной тип в параметре handleAs:
dojo.io.iframe.send({
form : dojo.byId("foo"),
handleAs : "text", //тип ответа сервера
url : "http://localhost:8080/upload/",
load : function(response, ioArgs) {
console.log(response, ioArgs); //ответ: "Thanks!"
return response;
},
error : function(response, ioArgs) {
console.log("error");
console.log(response, ioArgs);
return response;
}
});

Добавлено: 24 Июля 2018 20:51:53 Добавил: Андрей Ковальчук

Отправка формы с помощью IFRAME

Еще одно типичное использование плавающих фреймов IFRAME заключается в том, чтобы отправлять содержимое формы в фоновом режиме, причем это может быть даже форма выгрузки файла, что в обычном случае влечет за собой полную перезагрузку страницы. Ниже следует сценарий CherryPy, который обслуживает операцию выгрузки файла:

import cherrypy
# определите здесь, куда следует поместить выгружаемый файл
local_file_path="/tmp/uploaded_file"
class Content:
#обслуживает передачу файла...
@cherrypy.expose
def upload(self, inbound):
outfile = open(local_file_path, 'wb')
inbound.file.seek(0)
while True:
data = inbound.file.read(8192)
if not data:
break
outfile.write(data)
outfile.close()
# вернуть в качестве ответа простой файл HTML
return "<html><head></head>Thanks!</html>"
# запустить вебсервер, который прослушивает порт 8080
cherrypy.quickstart(Content(), '/')


А ниже приводится страница HTML, которая выполняет выгрузку. Когда выгрузка файла запускается с помощью IFRAME, она происходит в фоновом режиме, и сама страница при этом не изменяется, но если выгрузка файла инициируется с помощью кнопки отправки фо
мы, происходит переключение страницы. Обратите внимание, что в этом примере параметр запроса handleAs имеет значение "html".
<html>
<head>
<title>Fun with IFRAME Transports!</title>
<script type="text/javascript"
src="http://o.aolcdn.com/dojo/1.1/dojo/dojo.xd.js"
djConfig="isDebug:true,dojoBlankHtmlUrl:’/path/to/blank.html’">
</script>
<script type="text/javascript">
dojo.require("dojo.io.iframe");
dojo.addOnLoad(function() {
upload = function() {
dojo.io.iframe.send({
form : "foo",
handleAs : "html", //тип ответа сервера
url : "http://localhost:8080/upload/",
load : function(response, ioArgs) {
console.log(response, ioArgs);
return response;
},
error : function(response, ioArgs) {
console.log("error");
console.log(response, ioArgs);
return response;
}
});
};
});
</script>
</head>
(body)
<form id="foo" action="http://localhost:8080/upload/" method="post"
enctype="multipart/form-data">
<label for="file">Filename:</label>
<input type="file" name="inbound">
(br /)
<input type="submit" value="Submit Via The Form">
</form>
(button onclick="javascript:upload();")Submit Via the IFRAME Transport
(/button)
(/body)
</html>

Добавлено: 24 Июля 2018 20:50:40 Добавил: Андрей Ковальчук

Загрузка файлов с помощью IFRAME

Поскольку запуск загрузки файла посредством тега IFRAME является распространенной операцией, попробуем реализовать ее. Ниже приводится файл CherryPy, который отправляет локальный файл, когда происходит обращение по адресу http://localhost:8080/. Мы будем использовать этот адрес URL при обращении к серверу с помощью функции dojo.io.frame.send:

import cherrypy
from cherrypy.lib.static import serve_file
import os
# определите здесь абсолютный путь к файлу на вашей машине
local_file_path="/tmp/foo.html"
class Content:
#обслуживает передачу файла...
@cherrypy.expose
def download(self):
return serve_file(local_file_path, "application/x-download",
"attachment")
# запустить веб-сервер, который прослушивает порт 8080
cherrypy.quickstart(Content(), '/')


Ниже следует файл HTML, в котором используется IFRAME. Вы должны открыть его в броузере, и если в сценарии CherryPy правильно указан путь к этому файлу, то после щелчка на кнопке перед вами появится диалог загрузки файла.
<html>
<head>
<title>Fun with IFRAME Transports!</title>
<script type="text/javascript"
src="http://o.aolcdn.com/dojo/1.1/dojo/dojo.xd.js">
</script>
<script type="text/javascript">
dojo.require("dojo.io.iframe");
dojo.addOnLoad(function() {
download = function() {
dojo.io.iframe.send({
url : "http://localhost:8080/download/"
});
};
});
</script>
</head>
(body)
(button onclick="javascript:download()")Download!(/button)
(/body)
</html>

Добавлено: 24 Июля 2018 20:50:01 Добавил: Андрей Ковальчук

Получение программного кода JavaScript с помощью вызова JSONP

Как оказывается, функцию dojo.io.script.get можно использовать для взаимодействия с сервером, который возвращает чистый программ ный код JavaScript. В этом случае запрос выглядит точно так же, только вместо параметра callbackParamName следует определить значение параметра checkString. Значение параметра checkString представляет собой механизм, позволяющий проверить получение ответа. Если применение оператора typeof к имени, переданному в параметре checkString, не возвращает значение undefined, то можно предположить, что сценарий JavaScript был полностью загружен. (Другими словами, это грубый прием.) Предположим, что у нас имеется приведенный далее простой сценарий, использующий модуль CherryPy. Для проверки успешной загрузки сценария вы могли бы использовать значение o в параметре checkString, так как o – это переменная, которая, как предполагается, будет установлена в результате вызова JSONP (когда будет выполнено условие typeof(o) != undefined, можно будет предположить, что вызов завершился благополучно).

Сначала приведем сценарий CherryPy, возвращающий программный код JavaScript:

import cherrypy
class Content:
@cherrypy.expose
def index(self):
return "var o = {a : 1, b:2}"
cherrypy.quickstart(Content())


Предположим, что CherryPy прослушивает порт 8080, тогда соответствующая реализация получения программного кода JavaScript с использованием возможностей Dojo может выглядеть так:
dojo.require("dojo.io.script");
dojo.io.script.get({
checkString : "o",
timeout : 2000,
url : "http://localhost:8080",
load : function(response, ioArgs) {
console.log(o);
console.log(response)
},
error : function(response, ioArgs) {
console.log("error", response, ioArgs);
return response;
}
});

Добавлено: 24 Июля 2018 20:49:31 Добавил: Андрей Ковальчук

Подключение к источнику данных Flickr

Следующий пример иллюстрирует выполнение обращения JSONP к источнику данных Flickr. Попробуйте выполнить этот фрагмент в Firebug, чтобы увидеть, что происходит на самом деле. Кроме того, с помощью этого примера будет весьма поучительно исследовать ошибку, которая возникает, если опустить параметр callbackParamName (или указать неправильное значение):

dojo.require("dojo.io.script");
dojo.io.script.get({
callbackParamName : "jsoncallback", //определяется службой Flickr
url: "http://www.flickr.com/services/feeds/photos_public.gne",
content : {format : "json"},
load : function(response, ioArgs) {
console.log(response);
return response;
},
error : function(response, ioArgs) {
console.log("error");
console.log(response);
return response;
}
});

Добавлено: 24 Июля 2018 20:49:03 Добавил: Андрей Ковальчук

Использование JSONP вместе с Dojo

К этому моменту вы уже обладаете достаточным объемом знаний о Dojo, чтобы не удивляться тому, что инструментальный набор упрощает работу, связанную с применением приемов JSONP. Для достижения того же эффекта, что был продемонстрирован в предыдущем примере, можно было бы использовать функцию dojo.io.script.get, которая принимает те же параметры, что и различные методы XHR. Единственное, что следует отметить: параметр handleAs в действительности не применим к JSONP, и обязательно следует передать параметр call-backParamName, чтобы инструментарий Dojo мог передавать имя функции обратного вызова для организации ее вызова от вашего имени. Ниже демонстрируется, как это можно реализовать на практике:

//ресурс dojo.io.script не является частью Base, поэтому не забывайте
//явно подключать его к странице
dojo.require("dojo.io.script");
dojo.io.script.get({
callbackParamName : "c", //определяется службой jsonp
url: "http://example.com?id=23",
load : function(response, ioArgs) {
console.log(response);
return response;
},
error : function(response, ioArgs) {
console.log(response);
return response;
}
});


Следует пояснить, что callbackParamName задает имя параметра строки запроса, который определяется компанией example.com. Это не имя функции, которую вы определяете как функцию обратного вызова. Для этих целей Dojo создает временную функцию, с помощью которой передает полученные результаты в функцию load, следуя при этом соглашениям, принятым для функций XHR. Поэтому просто позвольте инструментарию Dojo самому разбираться с дополнением и обрабатывайте полученные результаты в функции load.

Добавлено: 24 Июля 2018 20:48:42 Добавил: Андрей Ковальчук

Пример использования JSONP

Как и все прочее, поначалу работа с JSONP кажется немного загадочной, но стоит только понять принцип действия, и все сразу будет выглядеть очень просто. Чтобы разобраться с этой концепцией, представим, что в странице, загруженной с адреса http://oreilly.com/, динамически создается тег SCRIPT, который добавляется в раздел HEAD. С источником этого тега происходит интересная метаморфоза: он свободно может загружать данные не только из домена oreilly.com, но из любого другого, например из http://example.com?id=23. Реализовать такую операцию на JavaScript достаточно просто:

e = document.createElement("SCRIPT");
e.src="http://example.com?id=23";
e.type="text/javascript";
document.getElementsByTagName("HEAD")[0].appendChild(e);


Обычно тег SCRIPT подразумевает загрузку фактического сценария, но в действительности можно организовать получение любого информационного наполнения, включая и объекты JSON. Однако здесь существует одна проблема – эти объекты можно применять только к разделу HEAD страницы, где невозможно реализовать чтонибудь интересное (разве что испортить внешний вид страницы).

Например, в результате применения этого приема можно получить страницу, как показано ниже, где жирным шрифтом выделен текст, выданный в результате выполнения предыдущего фрагмента JavaScript, который динамически добавляет тег SCRIPT в раздел HEAD:
<html>
<head>
<title>My Page</title>
<script type="text/javascript" >
{foo : "bar"}
</script>
</head>
(body)
Содержимое страницы.
(/body)
</html>


От добавления литерала объекта JavaScript в раздел HEAD мало проку, но представьте, что могло бы произойти, если бы можно было получить данные в формате JSON, содержащие вызов какойнибудь функции, точнее функции, которая уже определена гдето в теле страницы. Это могло бы способствовать достижению удивительных эффектов, потому что в этом случае можно было бы асинхронно запрашивать внешние данные и сразу же передавать их выбранной функции для обработки. Для достижения этого эффекта все, что необходимо, – это вставить тег SCRIPT, возвращающий не просто данные в формате JSON, такие как {foo : "bar"}, а данные, дополненные вызовом функции, например myCallback({foo : "bar"}). Если предположить, что функция myCallback уже определена, то, когда тег SCRIPT завершит загрузку данных, он вызовет функцию, которой передаст данные в виде параметра, фактически воспользовавшись ею как функцией обратного вызова. (Если что то осталось непонятным, стоит задержаться немного и представить себе, как происходит весь этот процесс.)

Но здесь остается еще одна маленькая проблема: как получить объект JSON, содержащий дополнение в виде вызова функции? Довольно просто: добрые дяди из example.com должны предоставить вам возможность указывать дополнительный параметр в строке запроса, позволяющий вам определять имя функции, вызов которой должен быть обернут в объект JSON. Допустим, что в соответствии с их решением, чтобы вызвать свою функцию, вы должны предоставить в строке запроса дополнительный параметр c (в котором указать имя своей функции); тогда обращение по адресу http://example.com?id=23&c=myCallback должно вернуть myCallback({foo : "bar"}). И это все, что требовалось сделать.

Добавлено: 24 Июля 2018 20:48:13 Добавил: Андрей Ковальчук

Утилиты для работы с формами и HTTP

Некоторые решения, основанные на технологии AJAX, при правильной реализации могут производить сильное впечатление, но не будем забывать, что некоторые проверенные элементы, такие как формы HTML, еще совсем не устарели и попрежнему играют важную роль во многих современных решениях, как с применением технологии AJAX, так и без нее. Библиотека Base предоставляет три функции преобразования форм:

dojo.formToObject(/*DOMNode||String*/ formNode) //Возвращает Object
dojo.formToQuery(/*DOMNode||String*/ formNode) //Возвращает String
dojo.formToJson(/*DOMNode||String*/ formNode) //Возвращает String


Чтобы продемонстрировать действие каждой из этих функций, предположим, что у нас имеется следующая форма:
<form id="register">
<input type="text" name="first" value="Foo">
<input type="button" name="middle" value="Baz" disabled>
<input type="text" name="last" value="Bar">
<select type="select" multiple name="favorites" size="5">
<option value="red">red</option>
<option value="green" selected>green</option>
<option value="blue" selected>blue</option>
</select>
</form>


Ниже приводятся результаты вызова каждой функции. Обратите внимание, что неактивные элементы формы были пропущены при преобразовании:
Функция formToObject вернула:
{
first: "Foo",
last : "Bar",
favorites: [
"green",
"blue"
]
};


Функция formToQuery вернула:
"first=Foo&last=Bar&favorites=green&favorites=blue"

Функция formToJson вернула:
'{"first": "Foo", "last": "Bar", "favorites": ["green", "blue"]}'


Библиотека Base предоставляет следующие вспомогательные функции, позволяющие выполнять преобразование строки запроса в объект и обратно. Они достаточно просты в применении, но не забывайте, что значения в строке запроса преобразуются в строки, даже если они фактически являются числами:
dojo.queryToObject(/*String*/ str) //Возвращает Object
dojo.objectToQuery(/*Object*/ map) //Возвращает String


Ниже приводится короткий фрагмент, иллюстрирующий применение этих функций:
//вернет объект {foo : "1", bar : "2", baz : "3"}
var o = dojo.queryToObject("foo=1&bar=2&baz=3");
//преобразует объект обратно в строку "foo=1&bar=2&baz=3"
dojo.objectToQuery(o);

Добавлено: 24 Июля 2018 20:47:34 Добавил: Андрей Ковальчук

DeferredList

Объект Deferred является частью библиотеки Base, а библиотека Core обеспечивает дополнительное приложение DeferredList, которое предоставляет средства управления множеством объектов Deferred. Среди типичных случаев применения DeferredList можно назвать:
• Когда необходимо запустить свою функцию обратного вызова или цепочку, после того как отработают все функции обратного вызова объектов Deferred в списке
• Когда необходимо запустить свою функцию обратного вызова или цепочку, если отработает хотя бы одна функция обратного вызова, принадлежащая какомулибо из объектов Deferred в списке
• Когда необходимо запустить свою функцию обработки ошибок или цепочку, если отработает хотя бы одна функция обработки ошибок, принадлежащая какомулибо из объектов Deferred в списке

Ниже приводится сигнатура функции DeferredList:

dojo.DeferredList(/*Array*/list, /*Boolean?*/fireOnOneCallback, /*Boolean?*/
fireOnOneErrback, /*Boolean?*/consumeErrors, /*Function?*/canceller)


Сигнатура достаточно очевидна: при вызове конструктора достаточно передать ему единственный аргумент – массив объектов Deferred. По умолчанию объект DeferredList запускает свою цепочку функций обратного вызова, только когда отработают цепочки функций обратного вызова всех объектов Deferred. Передавая логические аргументы, можно обеспечить запуск цепочек функций обратного вызова и функций обработки ошибок, если отработала хотя бы одна функция обратного вызова или функция обработки ошибок, соответственно.

Если в аргументе consumeErrors передать значение true, то ошибки будут поглощаться объектом DeferredList, что может быть удобно, когда нежелательно воспроизводить ошибки, порожденные отдельными объектами Deferred в списке. Аргумент canceller обеспечивает возможность передать свою собственную функцию отмены точно так же, как и в обычном объекте Deferred.

Добавлено: 24 Июля 2018 20:45:58 Добавил: Андрей Ковальчук

Собственная функция отмены

Любая функция XHR обладает специальной функцией отмены, которая вызывается при обращении к функции cancel(), но для собственных объектов Deferred можно создавать индивидуальные функции отмены, как показано ниже:

var canceller = function() {
console.log("custom canceller...");
//Если не вернуть собственный объект Error, по умолчанию будет //возвращен объект Error с сообщением "Deferred Cancelled"
}
var d = new dojo.Deferred(canceller); //передать функцию отмены в конструктор /* ....здесь происходит нечто интересное...*/
d.cancel(); // обработчики ошибок должны быть готовы особым образом // обработать объект Error с сообщением "Deferred Cancelled"

Добавлено: 24 Июля 2018 20:45:36 Добавил: Андрей Ковальчук

Внедрение объектов Deferred в функции XHR

Еще одна замечательная особенность объекта Deferred состоит в том, что он обеспечивает очевидный способ отмены асинхронного действия до его полного завершения. Ниже приводится доработанная версия нашего предыдущего примера, демонстрирующая возможность отменить уже выполняющийся запрос, а также – «внедрение» объекта Deferred в обработчики load и error:

<html>
<head>
<title>Fun with Deferreds!</title>
<script type="text/javascript"
src="http://o.aolcdn.com/dojo/1.1/dojo/dojo.xd.js">
</script>
<script type="text/javascript">
dojo.addOnLoad(function() {
var d = new dojo.Deferred;
//Добавить несколько функций обратного вызова
d.addCallback(
function(result) {
console.log("Callback 1 says that the result is ",
result);
return result;
}
);
d.addCallback(
function (result) {
console.log("Callback 2 says that the result is ",
result);
return result;
}
);
//Добавить несколько функций обработки ошибок
d.addErrback(
function(result) {
console.log("Errback 1 says that the result is ",
result);
return result;
}
);
d.addErrback(
function(result) {
console.log("Errback 2 says that the result is ",
result);
return result;
}
);
//Вызвать функцию, запускающую асинхронный запрос,
//которая вернет объект Deferred
request = dojo.xhrGet({
url: "http://localhost:8080",
timeout : 5000,
load : function(response, ioArgs) {
console.log("Load response is:", response);
console.log("Executing the callback chain now...");
//внедрить нашу цепочку функций обратного вызова
d.callback(response, ioArgs);
//позволить продолжить обработку цепочке объекта
//Deferred функции xhrGet...
return response;
},
error : function(response, ioArgs) {
console.log("Error!", response);
console.log("Executing the errback chain now...");
//внедрить нашу цепочку обработчиков ошибок
d.errback(response, ioArgs);
//позволить продолжить обработку цепочке объекта
//Deferred функции xhrGet...
return response;
}
});
});
</script>
</head>
(body)
XHR request in progress. You have about 3 seconds to cancel it.
(button onclick="javascript:request.cancel()")Cancel(/button)
(/body)
</html>


Если запустить этот пример, в консоли Firebug можно наблюдать следующий вывод:
xhrGet just fired. Waiting on callbacks or errbacks now...
Load response is: Hello
Executing the callback chain now...
Callback 1 says that the result is Hello
Callback 2 says that the result is Hello

А щелчок на кнопке «Cancel» (отмена) приведет к появлению следующих результатов:
xhrGet just fired. Waiting on callbacks or errbacks now...
Press the button to cancel...
Error: xhr cancelled dojoType=cancel message=xhr cancelleddojo.xd.js (line 20)
Error! Error: xhr cancelled dojoType=cancel message=xhr cancelled
Executing the errback chain now...
Errback 1 says that the result is Error: xhr cancelled dojoType=cancel
message=xhr
cancelled
Errback 2 says that the result is Error: xhr cancelled dojoType=cancel
message=xhr
cancelled

Добавлено: 24 Июля 2018 20:45:09 Добавил: Андрей Ковальчук

Использование объектов Deferred, возвращаемых функциями XHR

После запуска CherryPy и сохранения следующего ниже кода разметки в файле с именем foo.html в том же каталоге, где находится файл hello.py, все будет готово к испытаниям. Перейдите в броузере по адресу http://127.0.0.1:8080/foo.html, и страница foo.html должна загрузиться без какихлибо проблем:

<html>
<head>
<title>Fun with Deferreds!</title>
<script type="text/javascript"
src="http://o.aolcdn.com/dojo/1.1/dojo/dojo.xd.js">
</script>
<script type="text/javascript">
dojo.addOnLoad(function() {
//Вызвать функцию, запускающую асинхронный запрос,
//которая вернет объект Deferred
var d = dojo.xhrGet({
url: "http://localhost:8080",
timeout : 5000,
load : function(response, ioArgs) {
console.log("Load response is:", response);
console.log("Executing the callback chain now...");
return response;
},
error : function(response, ioArgs) {
console.log("Error!", response);
console.log("Executing the errback chain now...");
return response;
}
});
console.log("xhrGet fired. Waiting on callbacks or errbacks");
//Добавить несколько функций обратного вызова
d.addCallback(
function(result) {
console.log("Callback 1 says that the result is ",
result);
return result;
}
);
d.addCallback(
function (result) {
console.log("Callback 2 says that the result is ",
result);
return result;
}
);
//Добавить несколько обработчиков ошибок
d.addErrback(
function(result) {
console.log("Errback 1 says that the result is ",
result);
return result;
}
);
d.addErrback(
function(result) {
console.log("Errback 2 says that the result is ",
result);
return result;
}
);
});
</script>
</head>
(body)
Check the Firebug console.
(/body)
</html>


После запуска этого примера вы должны увидеть в консоли Firebug следующий вывод:
xhrGet fired. Waiting on callbacks or errbacks
Load response is: Hello
Executing the callback chain now...
Callback 1 says that the result is Hello
Callback 2 says that the result is Hello

Главный вывод, который следует из этого примера, заключается в том, что объект Deferred дает ясный, непротиворечивый интерфейс, позволяющий обрабатывать все ситуации, складывающиеся в результате работы функции xhrGet, – будь то успешное получение ответа на запросили ошибка, которую надо обработать.

Вы можете попробовать изменить значение предельного времени ожидания в вызове функции dojo.xhrGet так, чтобы это время оказалось меньше трех секунд, необходимых серверу для передачи ответа. В результате это приведет к появлению ошибки, и вы сможете увидеть, как запускается цепочка функций обработки ошибок. Эта цепочка запускается еще и тогда, когда чтото пошло не так как надо в одной из функций обратного вызова, поэтому вы можете попробовать внести в функцию обратного вызова программный код, генерирующий ошибку, чтобы увидеть, что перед вызовом цепочки функций обработки ошибок успевает отработать часть цепочки функций обратного вызова.

Добавлено: 24 Июля 2018 20:44:40 Добавил: Андрей Ковальчук

Исследование объекта Deferred с помощью CherryPy

Давайте для начала подготовим простую процедуру на стороне сервера, которая выполняет короткую задержку, а затем возвращает некоторое содержимое. (Пауза – это лишь способ подчеркнуть асинхронность поведения.) Ниже следует полное содержимое файла, использующего модуль CherryPy и реализующего упомянутую функциональность:

import cherrypy
from time import sleep
import os
# программный код, выполняющий запросы XHR, будет находиться
# в файле foo.html, и это видно в следующей директиве config
current_dir = os.getcwd()
config = {'/foo.html' :
{
'tools.staticfile.on' : True,
'tools.staticfile.filename' : os.path.join(current_dir, 'foo.html')
}
}
class Content:
# именно этот класс возвращает содержимое по запросу
@cherrypy.expose
def index(self):
sleep(3) # выдержать паузу в 3 сек. перед ответом
return "Hello"
# запустить вебсервер, который будет прослушивать порт 8080
cherrypy.quickstart(Content(), '/', config=config)


Предположим, что предыдущий программный код, использующий CherryPy, сохранен в файле с именем hello.py. Тогда, чтобы запустить сервер, достаточно в окне терминала выполнить команду python.hello.py. Чтобы убедиться, что сервер запущен, можно просто в броузере перейти по адресу http://127.0.0.1:8080/, после чего с короткой задержкой на экране должна появиться надпись «Hello».

Добавлено: 24 Июля 2018 20:44:13 Добавил: Андрей Ковальчук

Объекты Deferred

В настоящее время JavaScript не предусматривает поддержку концепции потоков выполнения, но существует возможность асинхронного выполнения запросов XMLHttpRequest с задержкой – с помощью функции setTimeout. Однако, чтобы запутаться в таком программном коде, потребуется оформить совсем немного асинхронных вызовов. Библиотека Base предоставляет класс с именем Deferred, который поможет справиться со сложными ситуациями, часто связанными с рутинными операциями реализации асинхронных событий. Подобно другим абстракциям, объекты Deferred позволяют прятать подробности реализации и/или шаблонный программный код за аккуратным и непротиворечивым интерфейсом.

Если попытаться описать ценность Deferred в одном предложении, оно могло бы звучать примерно так: эти объекты позволяют обрабатывать все операции сетевого вводавывода одинаковым способом независимо от того, являются ли они синхронными или асинхронными. Даже если объект Deferred уже запущен, потерпел неудачу или операция завершилась успехом, процедура составления цепочек функций обратного вызова и функций обработки ошибок остается неизменной. Как можно догадаться, такое поведение существенно упрощает работу.

Из ключевых особенностей объектов Deferred можно упомянуть возможность составлять цепочки из множества функций обратного вызова и функций обработки ошибок так, что они будут вызываться в заранее заданном порядке, а также возможность передавать объекту Deferred процедуру отмены, с помощью которой можно безопасно прерывать выполнение асинхронных запросов. Возможно, вы еще не заметили, но все функции XHR, представленные ранее в этой главе, возвращают объект Deferred, просто раньше у нас не было необходимости занимать ся его изучением. В действительности все сетевые операции вводавы вода в инструментальном наборе используют и возвращают объекты Deferred, потому что эти объекты обеспечивают высокую гибкость управления асинхронными операциями, которые являются результа том выполнения сетевых запросов.

Прежде чем вернуться к какимто из предыдущих попыток использования XHR, взгляните на следующий абстрактный пример, демонстрирующий применение объекта Deferred и составляющий основу некоторых концепций, которые нам предстоит рассматривать:

//Создать объект Deferred
var d = new dojo.Deferred(/* Здесь можно указать функцию отмены */);
//Добавить функцию обратного вызова
d.addCallback(function(response) {
console.log("The answer is", response);
return response;
});
//Добавить еще одну функцию обратного вызова,
//которая будет запускаться после предыдущей
d.addCallback(function(response) {
console.log("Yes, indeed. The answer is", response);
return response;
});
//Добавить функцию обработки ошибок на тот случай, если чтото пойдет не так
d.addErrback(function(response) {
console.log("An error occurred", response);
return response;
});
//Можно добавить еще столько функций обратного вызова
//и обработки ошибок, сколько потребуется
/* Некоторый программный код, выполняющий вычисления */
//Где-то в другом месте запускается цепочка функций обратного вызова
d.callback(46);

Если запустить этот пример в Firebug, можно увидеть следующий вы
вод:
The answer is 46
Yes, indeed. The answer is 46

Прежде чем перейти к более интересным примерам, наверное, имеет смысл ознакомиться с прикладным интерфейсом объекта Deferred.
Объект Deferred может переходить в состояние ошибки в любом из следующих трех случаев:
• Функция обратного вызова или функция обработки ошибок получила аргумент, который является объектом Error.
• Функция обратного вызова или функция обработки ошибок возбудила исключение.
• Функция обратного вызова или функция обработки ошибок вернула значение, которое является объектом Error.

Добавлено: 24 Июля 2018 20:43:35 Добавил: Андрей Ковальчук