Объект Deferred и побратимы
Работа с объектом "Deferred" это уже высший пилотаж, это "mad skills" заставлять асинхронный JavaScript работать синхронно. Давайте посмотрим как он работает (данный код можно скопировать в консоль и выполнить на любой странице, где подключен jQuery):
// инициализация Deferred объекта // статус "ожидает выполнение" var D = $.Deferred(); // подключаем обработчики D.done(function() { console.log("first") }); D.done(function() { console.log("second") }); // изменяем статус на "выполнен успешно" // для этого вызываем resolve() // наши обработчики будут вызваны в порядке очереди, // но они не ждут друг-друга D.resolve(); // данный обработчик подключён слишком поздно, и будет вызван сразу D.done(function() { console.log("third") });
Кроме сценариев с "happy end", есть ещё и грустные истории, когда всё пошло не так как нам бы хотелось:
// инициализация Deferred объекта var D = $.Deferred(); // подключаем обработчики D.done(function() { console.log("done") }); D.fail(function() { console.log("fail") }); // изменяем статус на "выполнен с ошибкой" D.reject(); // в консоли нас будет ожидать лишь "fail" :(
Для ускорения написания кода существует ещё метод "then()", который позволяет вешать одновременно обработчики всех типов:
D.then(function() { console.log("done") }, function() { console.log("fail") });
Становится ли от этого код читаемым – сомневаюсь, но метод есть, и может пригодится.
Ещё упомяну метод "always()" – он добавляет обработчики, которые будут выполнены вне зависимости от выбранного сценария. Чтобы не путаться в перечисленных методах приведу блок-схему:
В качестве аргументов методов "done()" и "fail()"может быть произвольное множество callback-функций. При вызове "resolve()" и "reject()" можно передать произвольные данные в зарегистрированные callback-функции для дальнейшей работы. Кроме того, существуют еще методы "resolveWith()" и "rejectWith()", они позволяют изменять контекст вызываемых callback-функции (т.е. внутри них "this" будет смотреть на указанный контекст)
Отдельно хотел отметить, что если вы собираетесь передать Deferred объект "на сторону", чтобы "там" могли повесить свои обработчики событий, но не хотите потерять контроль, то возвращайте не сам объект, а результат выполнения метода "promise()" – это фактически будет искомый объект в режиме "read only".
Теперь покажу хитрый метод "$.when()":
$.when( $.ajax("/ajax/example.json"), $("article").slideUp(200) ).then(function(){ alert("All done"); }, function(){ alert("Something wrong"); })
Поясню происходящее – AJAX запрос и анимация стартуют одновременно, когда и тот и другой завершат свою работу, будет вызвана функция, которую мы передаём в качестве аргумента в метод "then()" (одна из двух, в зависимости от исхода происходящего). Для обеспечения работы этой "магии" методы "when()", "ajax()" и "animate()" реализуют интерфейс Deferred. Пример работы
<!DOCTYPE html> <html dir="ltr" lang="en-US"> <head> <meta charset="UTF-8"/> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>Пример работы с $.when</title> <link rel="profile" href="http://gmpg.org/xfn/11"/> <link rel="shortcut icon" href="http://anton.shevchuk.name/favicon.ico"/> <link rel="stylesheet" href="css/styles.css"/> <script type="text/javascript" src="js/jquery.js"></script> <script type="text/javascript" src="js/code.js"></script> </head> <body> <div id="content" class="wrapper box"> <menu label="Try..."> <a href="deferred.html" title="go prev" class="button alignleft" rel="prev">← Back </a> <a href="index.html" title="back to Index" class="button alignleft" rel="index">Index §</a> <a href="#" title="reload" class="button alignleft" onclick="window.location.reload();return false">Reload ¤</a> <a href="deferred.pipe.html" title="go next" class="button alignright" rel="next">Next →</a> <hr/> <pre><code contenteditable="true">$.when( $(<span>'article img'</span>).slideUp(1000), $(<span>'article p'</span>).hide(3000) ).done(function(){ $(<span>'article'</span>).slideUp(500) })</code></pre> <button type="button" class="code">Run Code</button> </menu> <header> <h1>Пример работы метода $.when()</h1> <h2>Поддержка анимации</h2> </header> <article id="stick" class="box"> <h2>Article</h2> <p> <img src="images/photo-bumblebee-tumb.jpg" alt="Bumblebee" class="left" width="200"/> Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus rutrum, lectus eu varius consectetur, libero velit hendrerit augue, ut posuere enim neque in libero. Donec eget sagittis nibh. Suspendisse sed tincidunt urna. Cras quis euismod neque. Maecenas auctor ultricies posuere. Pellentesque luctus pulvinar dui eget semper. Donec sodales odio eu sapien varius luctus. Donec dictum feugiat diam at malesuada. Sed nec massa in augue condimentum faucibus quis ut diam. Quisque nisl sem, semper nec vulputate vel, mattis sit amet justo. Aliquam purus felis, tempor at scelerisque quis, tincidunt in neque. Etiam ut risus diam. Pellentesque fermentum risus id elit feugiat cursus. Ut fringilla dictum diam, sed iaculis lorem pulvinar ut. Cras vel elit id velit commodo viverra sit amet vel orci.</p> </article> <article> <h2>Article</h2> <p> <img src="images/photo-chamomile-tumb.jpg" alt="Chamomile" class="left" width="200"/> Duis in vestibulum sem. Cras euismod tincidunt dui, et scelerisque tellus condimentum vel. Maecenas et urna sit amet risus fermentum rhoncus nec porttitor ligula. Maecenas sit amet turpis enim, ut iaculis est. Duis feugiat, lacus id placerat porttitor, lorem augue gravida nisi, eu porta eros risus et lectus. Maecenas vestibulum nunc vel ipsum tincidunt sit amet blandit sapien bibendum. Proin vel vulputate nisl. Duis tempor imperdiet placerat. Pellentesque faucibus consequat magna, et bibendum nisl egestas non. Pellentesque sit amet mattis augue. Aenean at diam tincidunt purus sollicitudin gravida non in nisi. Fusce bibendum, magna in adipiscing mattis, sem risus fringilla mi, nec gravida lectus lectus at nibh. Suspendisse adipiscing elementum laoreet. Suspendisse sem erat, varius quis aliquet vitae, dapibus sed nibh. Nullam iaculis sem at mauris faucibus in vestibulum libero pretium. Aliquam eu turpis libero. Fusce et ultrices lectus.</p> </article> <article> <h2>Article</h2> <p> <img src="images/photo-maple-leaf-tumb.jpg" alt="Maple Leaf" class="left" width="200"/> Ut consequat commodo mauris, eu dignissim justo congue vel. Etiam commodo tincidunt diam, laoreet ullamcorper sapien egestas quis. Etiam auctor rutrum ante, at tincidunt elit lacinia non. Pellentesque molestie tellus sit amet est sodales nec rutrum leo pharetra. Donec lacinia ipsum vitae massa accumsan ullamcorper. Maecenas commodo lacus turpis. Proin sit amet mauris sem, imperdiet faucibus lorem. Fusce ullamcorper consectetur ligula vel pretium. Sed et elit vitae orci adipiscing condimentum id sed turpis. Morbi ultrices feugiat ullamcorper. Fusce at magna dolor. Sed sit amet risus massa, quis imperdiet libero. Proin justo purus, sodales nec cursus et, sollicitudin at nulla. Vivamus eget nibh tellus, sit amet facilisis ante.</p> </article> <footer> ©copyright 2014 Anton Shevchuk — <a href="http://anton.shevchuk.name/jquery-book/">jQuery Book</a> </footer> <script type="text/javascript"> var _gaq = _gaq || []; _gaq.push(['_setAccount', 'UA-1669896-2']); _gaq.push(['_trackPageview']); (function() { var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true; ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js'; var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s); })(); </script> </div> </body> </html>
Теперь можно и заумно – метод "when()" возвращает проекцию Deferred объекта, принимает в качестве параметров произвольное множество Deferred объектов, когда все из них отработают, объект "when" изменит своё состояние в "выполнено", с последующим вызовом всех подписавшихся.
А ещё, кроме поведения "ждём всех", с помощью Deferred можно выстраивать цепочки вызовов – "живые очереди":
$.ajax('ajax/example.json') .then(function(){ // подождём окончания AJAX запроса return $('article img').slideUp(2000) }) .then(function(){ // подождём пока спрячутся картинки return $('article p').slideUp(2000) }) .then(function(){ // подождём пока спрячутся параграфы return $('article').hide(2000); }) .done(function(){ // всё сделано шеф });
Подобное поведение можно воспроизвести используя лишь animate, но нам же хочется заглянуть чуть-чуть поглубже (до версии 1.8 тут шла речь о методе "pipe()", а теперь о "then()")
<!DOCTYPE html> <html dir="ltr" lang="en-US"> <head> <meta charset="UTF-8"/> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>Пример цепочек вызовов $.Deferred</title> <link rel="profile" href="http://gmpg.org/xfn/11"/> <link rel="shortcut icon" href="http://anton.shevchuk.name/favicon.ico"/> <link rel="stylesheet" href="css/styles.css"/> <script type="text/javascript" src="js/jquery.js"></script> <script type="text/javascript" src="js/code.js"></script> </head> <body> <div id="content" class="wrapper box"> <menu label="Try..."> <a href="when.html" title="go prev" class="button alignleft" rel="prev">← Back </a> <a href="index.html" title="back to Index" class="button alignleft" rel="index">Index §</a> <a href="#" title="reload" class="button alignleft" onclick="window.location.reload();return false">Reload ¤</a> <a href="callbacks.html" title="go next" class="button alignright" rel="next">Next →</a> <hr/> <pre><code contenteditable="true">$.ajax(<span>'ajax/example.json'</span>) .then(function(){ appendOut(<span>'Ajax\n'</span>); return $(<span>'article img'</span>).slideUp(2000) }) .then(function(){ appendOut(<span>'Images\n'</span>); return $(<span>'article p'</span>).slideUp(2000) }) .then(function(){ appendOut(<span>'Paragraph\n'</span>); return $(<span>'article'</span>).hide(2000); }) .done(function(){ appendOut(<span>"All Ok"</span>); }); </code></pre> <button type="button" class="code">Run Code</button> </menu> <header> <h1>Пример построение цепочек $.Deferred</h1> <h2>C animate(), метод pipe() не подружился :(</h2> </header> <div id="output"> <h3>Output</h3> <pre></pre> </div> <article> <h2>Article</h2> <p> <img src="images/photo-bumblebee-tumb.jpg" alt="Bumblebee" class="left" width="200"/> Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus rutrum, lectus eu varius consectetur, libero velit hendrerit augue, ut posuere enim neque in libero. Donec eget sagittis nibh. Suspendisse sed tincidunt urna. Cras quis euismod neque. Maecenas auctor ultricies posuere. Pellentesque luctus pulvinar dui eget semper. Donec sodales odio eu sapien varius luctus. Donec dictum feugiat diam at malesuada. Sed nec massa in augue condimentum faucibus quis ut diam. Quisque nisl sem, semper nec vulputate vel, mattis sit amet justo. Aliquam purus felis, tempor at scelerisque quis, tincidunt in neque. Etiam ut risus diam. Pellentesque fermentum risus id elit feugiat cursus. Ut fringilla dictum diam, sed iaculis lorem pulvinar ut. Cras vel elit id velit commodo viverra sit amet vel orci.</p> </article> <article> <h2>Article</h2> <p> <img src="images/photo-chamomile-tumb.jpg" alt="Chamomile" class="left" width="200"/> Duis in vestibulum sem. Cras euismod tincidunt dui, et scelerisque tellus condimentum vel. Maecenas et urna sit amet risus fermentum rhoncus nec porttitor ligula. Maecenas sit amet turpis enim, ut iaculis est. Duis feugiat, lacus id placerat porttitor, lorem augue gravida nisi, eu porta eros risus et lectus. Maecenas vestibulum nunc vel ipsum tincidunt sit amet blandit sapien bibendum. Proin vel vulputate nisl. Duis tempor imperdiet placerat. Pellentesque faucibus consequat magna, et bibendum nisl egestas non. Pellentesque sit amet mattis augue. Aenean at diam tincidunt purus sollicitudin gravida non in nisi. Fusce bibendum, magna in adipiscing mattis, sem risus fringilla mi, nec gravida lectus lectus at nibh. Suspendisse adipiscing elementum laoreet. Suspendisse sem erat, varius quis aliquet vitae, dapibus sed nibh. Nullam iaculis sem at mauris faucibus in vestibulum libero pretium. Aliquam eu turpis libero. Fusce et ultrices lectus.</p> </article> <article> <h2>Article</h2> <p> <img src="images/photo-maple-leaf-tumb.jpg" alt="Maple Leaf" class="left" width="200"/> Ut consequat commodo mauris, eu dignissim justo congue vel. Etiam commodo tincidunt diam, laoreet ullamcorper sapien egestas quis. Etiam auctor rutrum ante, at tincidunt elit lacinia non. Pellentesque molestie tellus sit amet est sodales nec rutrum leo pharetra. Donec lacinia ipsum vitae massa accumsan ullamcorper. Maecenas commodo lacus turpis. Proin sit amet mauris sem, imperdiet faucibus lorem. Fusce ullamcorper consectetur ligula vel pretium. Sed et elit vitae orci adipiscing condimentum id sed turpis. Morbi ultrices feugiat ullamcorper. Fusce at magna dolor. Sed sit amet risus massa, quis imperdiet libero. Proin justo purus, sodales nec cursus et, sollicitudin at nulla. Vivamus eget nibh tellus, sit amet facilisis ante.</p> </article> <footer> ©copyright 2014 Anton Shevchuk — <a href="http://anton.shevchuk.name/jquery-book/">jQuery Book</a> </footer> <script type="text/javascript"> var _gaq = _gaq || []; _gaq.push(['_setAccount', 'UA-1669896-2']); _gaq.push(['_trackPageview']); (function() { var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true; ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js'; var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s); })(); </script> </div> </body> </html>
В данном примере мы вызываем метод "then()", которому скормлена callback-функция, которая должна возвращать объект Deferred, это необходимо для соблюдения порядка в очереди – попробуйте убрать в примере один "return", и вы заметите, что следующая анимация наступит не дождавшись завершения предыдущей.
На этом возможности Deferred ещё не завершились, есть ещё связка методов "notify()" и "progress()" – первый шлёт послания в callback-функции, которые зарегистрированы с помощью второго. Приведу наглядный код для демонстрации (копи-паст в консоль, и смотрите что получается):
var D = $.Deferred(); var money = 100; // наш бюджет // съём денежки D.progress(function($){ console.log(money + " - " + $ + " = " + (money-$)); money -= $; if (money < 0) { // деньги закончились this.reject(); } }); // тратим деньги setTimeout(function(){ D.notify(40); }, 500); // покупка 1 setTimeout(function(){ D.notify(50); }, 1000); // покупка 2 setTimeout(function(){ D.notify(30); }, 1500); // покупка 3 D.done(function(){ console.info("All Ok") }); D.fail(function(){ console.error("Insufficient Funds") });
Испытайте всю мощь Deferred в примере
<!DOCTYPE html> <html dir="ltr" lang="en-US"> <head> <meta charset="UTF-8"/> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>Работаем с $.Deferred</title> <link rel="profile" href="http://gmpg.org/xfn/11"/> <link rel="shortcut icon" href="http://anton.shevchuk.name/favicon.ico"/> <link rel="stylesheet" href="css/styles.css"/> <style> .loading { background: url('images/ajax-loader.gif') no-repeat right; } #images div { height: 216px; width: 310px; float:left; overflow: hidden; margin: 4px; border: 1px solid #ccc; border-radius: 4px; -moz-border-radius: 4px; -webkit-border-radius: 4px; } article div img { min-height: 216px; min-width: 310px; border:0; padding:0; margin:0; } </style> <script type="text/javascript" src="js/jquery.js"></script> <script type="text/javascript" src="js/code.js"></script> <script> var Flickr = { search:function(query) { showLoader(); var def = $.Deferred(); $.getJSON("http://api.flickr.com/services/feeds/photos_public.gne?jsoncallback=?", { tags: query, tagmode: "any", format: "json" }, def.resolve ); return def.promise(); } }; function buildImages(data) { var def = $.Deferred(); var limit = 4; var loaded = 0; $.each(data.items, function(i, item){ if ( i == limit ) return false; var img = new Image(); img.onload = img.onerror = function() { // изменяем прогресс загрузки def.notify(1) }; img.src = item.media.m; var div = $('<div></div>').append(img); $(div).prependTo("#images"); }); def.progress(function() { loaded ++; if (limit == loaded) { def.resolve(); } }); def.done(hideLoader); } function showLoader() { $('h1').addClass('loading'); $('#images').slideUp(function(){ $(this).html('') }); } function hideLoader() { $('h1').removeClass('loading'); $('#images').slideDown(); } </script> </head> <body> <div id="content" class="wrapper box"> <menu label="Try..."> <a href="index.html" title="go prev" class="button alignleft" rel="prev">← Back </a> <a href="index.html" title="back to Index" class="button alignleft" rel="index">Index §</a> <a href="#" title="reload" class="button alignleft" onclick="window.location.reload();return false">Reload ¤</a> <a href="when.html" title="go next" class="button alignright" rel="next">Next →</a> <hr/> <pre><code contenteditable="true">Flickr .search(<span>'orange'</span>) .then(buildImages)</code></pre> <button type="button" class="code">Run Code</button> </menu> <header> <h1>Пример работы с $.Deferred</h1> <h2>С использованием AJAX и ожиданием подгрузки картинок. Код функции buildImages вы найдёте в исходном коде данной страницы</h2> </header> <article> <h2>Flickr</h2> <p>Попробуем загрузить картинки используя AJAX+JSONP. По получению ответа с сервера создаём объекты «Image» и ждём их загрузки, и лишь после этого отображаем готовый результат</p> <div id="images"> </div> </article> <footer> ©copyright 2014 Anton Shevchuk — <a href="http://anton.shevchuk.name/jquery-book/">jQuery Book</a> </footer> <script type="text/javascript"> var _gaq = _gaq || []; _gaq.push(['_setAccount', 'UA-1669896-2']); _gaq.push(['_trackPageview']); (function() { var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true; ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js'; var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s); })(); </script> </div> </body> </html>
Callbacks
Callbacks – это крутой объект – он позволяет составлять списки функций обратного вызова, а также даёт бразды правления над ними. Работать с ним проще нежели с Deferred, тут нет разделения на позитивный и негативный сценарии, лишь стек функций, который будет выполнен по команде "fire()":
var C = $.Callbacks(); C.add(function(msg) { console.log(msg+" first") }); C.add(function(msg) { console.log(msg+" second") }); C.fire("Go"); >>> Go first Go second
— А в чём сила, брат?
— В аргументах
По умолчанию, вы можете прямо из консоли вызвать метод "fire()" снова и снова, и будете получать один и тот же результат раз за разом. А можно задать поведение Callbacks через флаги:
- once – все функции будут вызваны единожды (аналогично как в Deferred).
- memory – сохранять значение с последнего вызова "fire()", и скармливать его в ново-зарегистрированные функции обратного вызова, и лишь потом обрабатывает новое значение (в Deferred именно так).
- unique – список функций обратного вызова фильтруется по уникальности
- stopOnFalse – как только какая-нить функция вернёт "false", процесс запуска остановится
Наверное, будет лучше с примерами, вот "once":
var C = $.Callbacks("once"); C.add(function(msg) { console.log(msg+" first") }); C.add(function(msg) { console.log(msg+" second") }); C.fire("Go"); C.fire("Again"); // не даст результата, только Go >>> Go first Go second
C "memory" посложнее, будьте внимательней:
var C = $.Callbacks("memory"); C.add(function(msg) { console.log(msg+" first") }); C.fire("Go"); C.add(function(msg) { console.log(msg+" second") }); C.fire("Again"); >>> Go first Go second // без флага, этой строчки не было бы Again first Again second
Пример с уникальностью прост до безобразия:
var C = $.Callbacks("unique"); var func = function(msg) { console.log(msg+" first") }; C.add(func); C.add(func); // эта строка не повлияет на результат C.fire("Go"); // только Go first >>> Go first
Флаг "stopOnFalse":
var C = $.Callbacks("stopOnFalse"); C.add(function(msg) { console.log(msg+" first"); return false; // вот он – роковой false }); C.add(function(msg) { console.log(msg+" second") }); C.fire("Go"); // только Go first >>> Go first
Перечисленные флаги можно комбинировать и получать интересные результаты, а можно не получать, а лишь посмотреть на пример:
<!DOCTYPE html> <html dir="ltr" lang="en-US"> <head> <meta charset="UTF-8"/> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>Пример использования с $.Callbacks</title> <link rel="profile" href="http://gmpg.org/xfn/11"/> <link rel="shortcut icon" href="http://anton.shevchuk.name/favicon.ico"/> <link rel="stylesheet" href="css/styles.css"/> <style> article { overflow: visible; min-height: 120px; margin: 10px; position: relative; } #car { width: 100px; height: 100px; position: absolute; background: url(images/car.png) 50% 50% no-repeat; -webkit-transition: all 0.2s ease-in-out; -moz-transition: all 0.2s ease-in-out; -o-transition: all 0.2s ease-in-out; -ms-transition: all 0.2s ease-in-out; transition: all 0.2s ease-in-out; } .up { -webkit-transform: rotate(0deg); -moz-transform: rotate(0deg); -o-transform: rotate(0deg); -ms-transform: rotate(0deg); transform: rotate(0deg); } .down { -webkit-transform: rotate(180deg); -moz-transform: rotate(180deg); -o-transform: rotate(180deg); -ms-transform: rotate(180deg); transform: rotate(180deg); } .right { -webkit-transform: rotate(90deg); -moz-transform: rotate(90deg); -o-transform: rotate(90deg); -ms-transform: rotate(90deg); transform: rotate(90deg); } .left { -webkit-transform: rotate(270deg); -moz-transform: rotate(270deg); -o-transform: rotate(270deg); -ms-transform: rotate(270deg); transform: rotate(270deg); } </style> <script type="text/javascript" src="js/jquery.js"></script> <script type="text/javascript" src="js/code.js"></script> <script> var C = $.Callbacks(); var car; $(function(){ car = $('#car'); C.add(function(speed){ appendOut('\nSpeed 200px per '+speed+' ms: ') }); $(document).keydown(function(e){ // 37 - left // 38 - up // 39 - right // 40 - down switch (e.keyCode) { case 37: C.add(function(speed){ car.queue(function(){ $(this).attr('class', 'left') .dequeue(); }); car.animate({left:'-=200'}, speed); appendOut('← '); }); appendOut('\nAdded ←'); break; case 38: C.add(function(speed){ car.queue(function(){ $(this).attr('class', 'up') .dequeue(); }).animate({top:'-=200'}, speed); appendOut('↑ '); }); appendOut('\nAdded ↑'); break; case 39: C.add(function(speed){ car.queue(function(){ $(this).attr('class', 'right') .dequeue(); }); car.animate({left:'+=200'}, speed); appendOut('→ '); }); appendOut('\nAdded →'); break; case 40: C.add(function(speed){ car.queue(function(){ $(this).attr('class', 'down') .dequeue(); }); car.animate({top:'+=200'}, speed); appendOut('↓ '); }); appendOut('\nAdded ↓'); break; } }) }); </script> </head> <body> <div id="content" class="wrapper box"> <menu label="Try..."> <a href="deferred.pipe.html" title="go prev" class="button alignleft" rel="prev">← Back </a> <a href="index.html" title="back to Index" class="button alignleft" rel="index">Index §</a> <a href="#" title="reload" class="button alignleft" onclick="window.location.reload();return false">Reload ¤</a> <hr/> <pre><code><em>// use button up arrow: ↑</em> C.add(function(speed){ car.animate({top:'-=200px'}, speed) })</code></pre> <pre><code><em>// use button down arrow: ↓</em> C.add(function(speed){ car.animate({top:'+=200px'}, speed) })</code></pre> <pre><code><em>// use button right arrow: →</em> C.add(function(speed){ car.animate({left:'+=200px'}, speed) })</code></pre> <pre><code><em>// use button left arrow: ←</em> C.add(function(speed){ car.animate({left:'-=200px'}, speed) })</code></pre> <pre><code contenteditable="true">C.fire(200)</code></pre> <button type="button" class="code">Run Code</button> </menu> <header> <h1>Пример работы $.Callbacks</h1> <h2>На практике мне ещё не приходилось использовать, поэтому пример немного надуманный - запрограммируйте поездку машинки используя стрелки на клавиатуре, а затем запустите программу, указав скорость</h2> </header> <div id="output"> <h3>Output</h3> <pre></pre> </div> <article> <div id="car"></div> </article> <footer> ©copyright 2014 Anton Shevchuk — <a href="http://anton.shevchuk.name/jquery-book/">jQuery Book</a> </footer> <script type="text/javascript"> var _gaq = _gaq || []; _gaq.push(['_setAccount', 'UA-1669896-2']); _gaq.push(['_trackPageview']); (function() { var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true; ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js'; var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s); })(); </script> </div> </body> </html>
Из истории: объект "Deferred" отпочковался от метода "ajax()" в результате рефакторинга версии 1.5. Шло время, появлялись новые версии jQuery, и вот новый виток рефакторинга – результатом стало отделение "Callbacks" от "Deferred" в версии 1.7, таким образом в текущей версии библиотеки метод "ajax()" работает с объектом "Deferred", который является надстройкой над "Callbacks". Дабы не вносить путаницу в терминологию, я использую определение "Deferred Callbacks" и при работе с "Callbacks", ибо колбэков много, и каждый раз уточнять, что я говорю именно "о том самом" - дело достаточно утомительное.
Статьи по данной теме:
- "Что такое этот новый jQuery.Callbacks Object" [http://habrahabr.ru/post/135821/]
- "jQuery Deferred Object (подробное описание)" [http://habrahabr.ru/post/113073/]
- "Async JS: The Power of $.deffered" [http://www.html5rocks.com/en/tutorials/async/deferred/]