Catch 22 AJAX — это, ради простоты, большую часть времени, когда мы хотим писать «синхронный код», но асинхронный — единственный способ избежать некоторых довольно неприятных проблем с юзабилити . Это означает, вместо того, чтобы писать простой код, как нам хотелось бы, например;
function doClick() { var xmlhttp = new XMLHttpRequest(); xmlhttp.open("GET","http://example.com/products",false); # Execution blocks here, waiting for the response to complete... xmlhttp.send(null); alert(xmlhttp.responseText); }
… вместо этого мы должны обрабатывать это с помощью обратных вызовов, простейшим примером является …
var xmlhttp = new XMLHttpRequest(); function doClick() { xmlhttp.open("GET","http://example.com/products",true); // Set the callback xmlhttp.onreadystatechange = handleResponse; xmlhttp.send(null); } function handleResponse() { if ( xmlhttp.readyState == 4 ) { alert (xmlhttp.responseText); } }
var xmlhttp = new XMLHttpRequest(); function doClick() { xmlhttp.open("GET","http://example.com/products",true); // Set the callback xmlhttp.onreadystatechange = handleResponse; xmlhttp.send(null); } function handleResponse() { if ( xmlhttp.readyState == 4 ) { alert (xmlhttp.responseText); } }
… но теперь это принесло больше проблем. Обратный вызов теперь зависит от того, доступен ли глобальный объект xmlhttp (и глобальные переменные для любого значительного проекта обычно являются злыми). А что если пользователь doClick()
функцию doClick()
? А как насчет асинхронных запросов, которые приостанавливаются для перерыва на кофе, а затем неожиданно возвращаются гораздо позже (требуется тайм-аут)? И это только для начала.
В любом случае — пара интересных проектов работают над тем, чтобы дать нам лучшее из обоих миров — асинхронные запросы, но (как выглядит) блокирующий код. Оба работают на основе расширения самого Javascript, так что то, что раньше было огромным усилием в ручном кодировании, стало аккуратно скрыто за новым оператором или ключевым словом Javascript.
Повествовательный Javascript
Первый — это Narrative Javascript, который добавляет новый «оператор блокировки» ->
так что ваш код становится чем-то вроде;
function doClick() { # Note the blocking operator... var response = doHttpRequest->("http://example.com/products"); alert(response); }
function doClick() { # Note the blocking operator... var response = doHttpRequest->("http://example.com/products"); alert(response); }
Обзор Narrative JS является хорошей отправной точкой. Возможно, преимуществом Narrative JS является то, что он является чистым Javascript — хотя в документах рекомендуется проводить предварительную обработку в автономном режиме с использованием чего-то вроде Rhino , вы, вероятно, (теоретически) можете предварительно обработать свой код по требованию (с потенциально значительными потерями производительности) в браузере, поскольку синтаксический анализатор Javascript нарцисс (также чистый JS). По крайней мере, тот факт, что JS — все, может сделать людей более уверенными в использовании.
jwacs
Второй — jwacs — Javascript с расширенной поддержкой продолжения . Это на самом деле идет намного дальше, чем просто возможность симулировать код блокировки, добавив четыре новых ключевых слова (и оператор импорта). Более ранний пример (с помощью связанного API утилиты jwacs) становится;
function doClick() { var response = JwacsLib.fetchData("GET", "http://example.com/products")) alert(response); }
function doClick() { var response = JwacsLib.fetchData("GET", "http://example.com/products")) alert(response); }
Чтобы увидеть расширенный Javascript, вам нужно взглянуть на приведенное выше определение fetchData
;
fetchData: function(method, url) { var http = JwacsLib.getHttpObj(); var k = function_continuation; http.onreadystatechange = function() { try { // Report results to the continuation on completion if(http.readyState == 4) { // Check for errors if(!(http.status == undefined || http.status == 0 || (http.status >= 200 && http.status < 300))) { var err = new Error("Server returned " + http.status); throw err; } // No errors, so resume the continuation with the raw results http.onreadystatechange = JwacsLib.emptyFunction; resume k <- http.responseText; } } catch(e) { // Errors are thrown as exceptions into the continuation http.onreadystatechange = null; throw e -> k; } }; http.open(method, url); http.send(null); suspend; }
fetchData: function(method, url) { var http = JwacsLib.getHttpObj(); var k = function_continuation; http.onreadystatechange = function() { try { // Report results to the continuation on completion if(http.readyState == 4) { // Check for errors if(!(http.status == undefined || http.status == 0 || (http.status >= 200 && http.status < 300))) { var err = new Error("Server returned " + http.status); throw err; } // No errors, so resume the continuation with the raw results http.onreadystatechange = JwacsLib.emptyFunction; resume k <- http.responseText; } } catch(e) { // Errors are thrown as exceptions into the continuation http.onreadystatechange = null; throw e -> k; } }; http.open(method, url); http.send(null); suspend; }
Обратите внимание на function_continuation
, suspend
, resume
и расширенный бросок: throw e -> k;
над. Препроцессор jwacs написан на LISP …
Так каково общее чувство здесь? Рассматриваете ли вы использовать их?
Само понятие расширения Javascript новым синтаксисом может быть оскорбительным для многих. Вы также ввели некоторые существенные зависимости — последующее изменение плана может привести к значительным переписываниям (и, конечно, они оба все еще являются прототипом).
В то же время, написание чего-нибудь нетривиального в Javascript, включающего асинхронную обработку и обратные вызовы, может быстро стать кошмаром — почему бы не устранить человеческие усилия с помощью некоторого умного синтаксиса? Общий подход здесь кажется мне хорошим.
webtuesday
Пока я здесь — короткая реклама сегодняшнего веб- дня с Патрисом, рассказывающим о его опыте веб-тестирования с Selenium (это не просто « больше Java », вы знаете;) в штаб- квартире Tilllate .