Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
branch: master
Fetching contributors…

Cannot retrieve contributors at this time

1285 lines (1181 sloc) 63.89 kb
<!DOCTYPE html>
<html>
<head>
<title>JSDeferred - Asynchronous library in JavaScript. Standalone and Compact</title>
<meta charset="utf-8"/>
<link href='http://fonts.googleapis.com/css?family=Buenard' rel='stylesheet' type='text/css'/>
<link rel="stylesheet" type="text/css" href="doc/styles.css"/>
<script type="text/javascript" src="http://code.jquery.com/jquery-1.7.1.min.js"></script>
<script type="text/javascript" src="http://cdn.washer-in-the-rye.com/jsdeferred.jquery.js"></script>
<script type="text/javascript" src="http://cdn.washer-in-the-rye.com/mockxhr/mockxhr.js"></script>
<script type="text/javascript" src="doc/scripts.js"></script>
<script type="text/javascript">
window.XMLHttpRequest.HANDLERS['README.markdown'] = function (xhr, data) {
xhr.responseText = 'README.markdown';
};
window.XMLHttpRequest.HANDLERS['ChangeLog'] = function (xhr, data) {
xhr.responseText = 'ChangeLog';
};
</script>
<!--[if lt IE 9]>
<script src="http://html5shiv.googlecode.com/svn/trunk/html5.js"></script>
<![endif]-->
<script type="text/javascript">
var _gaq = _gaq || [];
_gaq.push(['_setAccount', 'UA-7079167-9']);
_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>
</head>
<body>
<div id="whole">
<header id="top">
<h1><a href="/">JSDeferred</a></h1>
<p class="description">Standalone and Compact asynchronous library in JavaScript.</p>
</header>
<nav id="global-navigation">
<div id="global-navigation-inner">
<ul>
<li class="top"><a href="#top">JSDeferred <span class="sub">- asynchronous library</span></a></li>
<li><a href="#overview">Overview</a></li>
<li><a href="#download">Download</a></li>
<li><a href="#tutorial">Tutorial</a></li>
<li><a href="#cookbook">Cookbook</a></li>
<li><a href="#behavior">Behavior</a></li>
<li><a href="#development">Development</a></li>
<li><a href="./doc/Deferred.html">API</a></li>
</ul>
</div>
</nav>
<div id="content">
<div id="content-inner">
<div class="line">
<section class="toplevel grid-6" id="overview">
<h1>Overview</h1>
<p>
JSDeferred is standalone and compact asynchronous library.
Asynchronous codes are very confusing because it is a storm of callbacks.
JSDeferred improve the readability of asynchronous codes by providing one object and some functions.
</p>
<pre>
next(<span class="Identifier">function</span> () {
alert(<span class="Constant">'Hello,'</span>);
<span class="Statement">return</span> wait(<span class="Constant">3</span>);
}).
next(<span class="Identifier">function</span> (r) {
alert(<span class="Constant">'World!'</span>);
});
</pre>
<p>"Standalone and Compact" means JSDeferred is very portable so works in many environment.</p>
</section><!-- Overview -->
<section class="toplevel grid-6" id="download">
<h1>Download</h1>
<ul class="line">
<li class="grid-2">
<a href="https://raw.github.com/cho45/jsdeferred/master/jsdeferred.jquery.js">
<h1>With jQuery</h1>
<p>For web use and jQuery users.</p>
</a>
</li>
<li class="grid-2">
<a href="https://raw.github.com/cho45/jsdeferred/master/jsdeferred.nodoc.js">
<h1>Plain Edition</h1>
<p>For generic use. Web without jQuery or etc...</p>
</a>
</li>
<li class="grid-2">
<a href="https://raw.github.com/cho45/jsdeferred/master/jsdeferred.userscript.js">
<h1>For userscripts</h1>
<p>For use <code>with(D()) { ... }</code></p>
</a>
</li>
<li class="grid-2">
</li>
<li class="grid-2">
<a href="https://raw.github.com/cho45/jsdeferred/master/jsdeferred.js">
<h1>Documented</h1>
<p>Full documented version.</p>
</a>
</li>
<li class="grid-2">
<a href="https://github.com/cho45/jsdeferred/tree/master/binding">
<h1>Others</h1>
<p>Other resources.</p>
</a>
</li>
</ul>
</section><!-- Download -->
<section class="toplevel grid-12" id="supported">
<h1>Supported Environment</h1>
<ul class="line">
<li class="grid-2">
<h1>Browsers</h1>
<p>
Offcourse, almost browsers are supported:
<a href="http://www.google.com/chrome/">Google Chrome</a>,
<a href="http://www.opera.com/">Opera,</a>
<a href="http://windows.microsoft.com/en-us/internet-explorer/products/ie/home">Microsoft Internet Explorer</a> and etc.
</p>
</li>
<li class="grid-2">
<h1><a href="http://nodejs.org/">node.js</a></h1>
<p>
node.js provides many many asynchronous functions. JSDeferred helps program readability.
</p>
</li>
<li class="grid-2">
<h1><a href="http://code.google.com/chrome/extensions/index.html">Chrome Extension</a></h1>
<p>
Google Chrome provides HTML5 based extension feature. JSDeferred can be used with it.
</p>
</li>
<li class="grid-2">
</li>
<li class="grid-2">
<h1><a href="http://www.appcelerator.com/products/titanium-mobile-application-development/">Titanium</a></h1>
<p>
Titanium Mobile SDK can create iPhone and Android application by JavaScript. JSDeferred can work in it.
</p>
</li>
<li class="grid-2">
<h1><a href="http://www.greasespot.net/">Greasemonkey</a></h1>
<p>
JSDeferred is standalone and compact. So you can just copy and paste your userscripts.
</p>
</li>
</ul>
<p>In fact, JSDeferred only depends on setTimeout() function, so if setTimeout is provided, JSDeferred will work.</p>
</section><!-- Supported -->
<section class="toplevel grid-12" id="tutorial">
<h1>Tutorial</h1>
<section>
<h1>Loading Scripts</h1>
<p>To use JSDeferred, add a script element to the HTML</p>
<pre>
<span class="Identifier">&lt;</span><span class="Identifier">script</span><span class="Identifier"> </span><span class="Type">type</span>=<span class="Constant">&quot;text/javascript&quot;</span><span class="Identifier"> </span><span class="Type">src</span>=<span class="Constant">&quot;jsdeferred.js&quot;</span><span class="Identifier">&gt;</span><span class="Identifier">&lt;/script&gt;</span>
<span class="Identifier">&lt;</span><span class="Identifier">script</span><span class="Identifier"> </span><span class="Type">type</span>=<span class="Constant">&quot;text/javascript&quot;</span><span class="Identifier"> </span><span class="Type">src</span>=<span class="Constant">&quot;my.js&quot;</span><span class="Identifier">&gt;</span><span class="Identifier">&lt;/script&gt;</span>
</pre>
<p>JSDeferred is stand-alone, and does not depend on any external libraries, so that loading jsdeferred.js is sufficient in order to use it. The codes below are what would be written in my.js.</p>
<h2>The First Step</h2>
<p>Loading JSDeferred defines the Deferred object.
For convenience, we export a set of functions to the global scope by Deferred.define(). You don't, of course, need to export those functions at all.</p>
<pre>
Deferred.define();
</pre>
<p>By doing this, you can use useful functions such as next(), loop(), call(), parallel() and wait() as global functions.
Let's write some asynchronous process. </p>
<pre>
next(<span class="Identifier">function</span> () {
alert(<span class="Constant">&quot;Hello!&quot;</span>);
<span class="Statement">return</span> wait(<span class="Constant">5</span>);
}).
next(<span class="Identifier">function</span> () {
alert(<span class="Constant">&quot;World!&quot;</span>);
});
</pre>
<p>This is a process that alerts <code>Hello!</code>, then alerts <code>World!</code> after 5 seconds of delay.</p>
<p>The above code is exactly the same as the following. It can be used even if you don't choose to export the functions using Deferred.define().
</p>
<pre>
Deferred.next(<span class="Identifier">function</span> () {
alert(<span class="Constant">&quot;Hello!&quot;</span>);
<span class="Statement">return</span> Deferred.wait(<span class="Constant">5</span>);
}).
next(<span class="Identifier">function</span> () {
alert(<span class="Constant">&quot;World!&quot;</span>);
});
</pre>
</section>
<section>
<h1>Comparison with ordinary callback processes</h1>
<p>What's the advantage of writing in such style.</p>
<p>If you give a function as a callback, then asynchronous processes are written as a nest of functions. For example, if you want to fetch /foo.json, /bar.json, /baz.json in this order.</p>
<pre>
<span class="Comment">// http.get is assumed to be a function that takes a URI and a callback function as arguments</span>
http.get(<span class="Constant">&quot;/foo.json&quot;</span>, <span class="Identifier">function</span> (dataOfFoo) {
http.get(<span class="Constant">&quot;/bar.json&quot;</span>, <span class="Identifier">function</span> (dataOfBar) {
http.get(<span class="Constant">&quot;/baz.json&quot;</span>, <span class="Identifier">function</span> (dataOfBaz) {
alert([dataOfFoo, dataOfBar, dataOfBaz]);
});
});
});
</pre>
<p>You see here that the nesting of functions get deeper and deeper as you have more asynchronous processes. What if you want get an arbitrary number of data?</p>
<pre>
<span class="Type">var</span> wants = [<span class="Constant">&quot;/foo.json&quot;</span>, <span class="Constant">&quot;/bar.json&quot;</span>, <span class="Constant">&quot;/baz.json&quot;</span>];
<span class="Comment">// How would you write that?</span>
</pre>
<p>It's too cumbersome to do it. Let's do it with the Deferred.</p>
<pre>
<span class="Comment">// http.get is assumed to be a function that takes a URI as an argument and returns a Deferred instance</span>
<span class="Type">var</span> results = [];
next(<span class="Identifier">function</span> () {
<span class="Statement">return</span> http.get(<span class="Constant">&quot;/foo.json&quot;</span>).next(<span class="Identifier">function</span> (data) {
results.push(data);
});
}).
next(<span class="Identifier">function</span> () {
<span class="Statement">return</span> http.get(<span class="Constant">&quot;/baz.json&quot;</span>).next(<span class="Identifier">function</span> (data) {
results.push(data);
});
}).
next(<span class="Identifier">function</span> () {
<span class="Statement">return</span> http.get(<span class="Constant">&quot;/baz.json&quot;</span>).next(<span class="Identifier">function</span> (data) {
results.push(data);
});
}).
next(<span class="Identifier">function</span> () {
alert(results);
});
</pre>
<p>The code is longer, but the processes are in serial. I can even combine the part occurring three times.</p>
<pre>
<span class="Type">var</span> wants = [<span class="Constant">&quot;/foo.json&quot;</span>, <span class="Constant">&quot;/bar.json&quot;</span>, <span class="Constant">&quot;/baz.json&quot;</span>];
<span class="Type">var</span> results = [];
loop(wants.length, <span class="Identifier">function</span> (i) {
<span class="Statement">return</span> http.get(wants[i]).next(<span class="Identifier">function</span> (data) {
results.push(data);
});
}).
next(<span class="Identifier">function</span> () {
alert(results);
});
</pre>
<p>Now it's shorter, and can handle any number of requests. "loop" is a function that, if a Deferred instance is returned in the argument function,
it waits until the deferred process to finish and then execute the following process.</p>
<p>The above is a code to fire requests sequentially, i.e. load foo.json then bar.json and so on,
you probably have more situations where you want to load them all at once. In that case, you can simply write as this.</p>
<pre>
parallel([
http.get(<span class="Constant">&quot;/foo.json&quot;</span>),
http.get(<span class="Constant">&quot;/bar.json&quot;</span>),
http.get(<span class="Constant">&quot;/baz.json&quot;</span>)
]).
next(<span class="Identifier">function</span> (results) {
alert(results);
});
</pre>
<p>"parallel" is a function that executes the following process after all Deferred instances have finished their processes. It's that simple, isn't it?</p>
</section>
<section>
<h1>Error Handling</h1>
<p>What's useful about Deferred is its error handling. Browsers like Firefox kills errors occurred during an asynchronous process without raising the error console.
How would you debug such a case?</p>
<p>I normally surround the asynchronous process with a "try {} catch (e) { alert(e) }", but it's tedious to do it every time.</p>
<p>JSDeferred can create an error-back flow apart from its normal callback flow. For example, have a code like this.</p>
<pre>
next(<span class="Identifier">function</span> () {
<span class="Comment"> // something 1</span>
}).
next(<span class="Identifier">function</span> () {
<span class="Comment"> // asynchronous process</span>
<span class="Statement">throw</span> <span class="Constant">&quot;error!&quot;</span>;
}).
next(<span class="Identifier">function</span> () {
<span class="Comment"> // something 2 (not executed as an error occurs in the previous process)</span>
});
</pre>
<p>Now you want to handle exceptions.</p>
<pre>
next(<span class="Identifier">function</span> () {
<span class="Comment"> // something 1</span>
}).
next(<span class="Identifier">function</span> () {
<span class="Comment"> // asynchronous process</span>
<span class="Statement">throw</span> <span class="Constant">&quot;error!&quot;</span>;
}).
next(<span class="Identifier">function</span> () {
<span class="Comment"> // something 2 (not executed as an error occurs in the previous process)</span>
}).
error(<span class="Identifier">function</span> (e) {
alert(e);
});
</pre>
<p>You just need add .error(). It can catch all the exceptions that occur before the .error() part.</p>
<p>In the above code the "something 2" won't be executed because of the exception, but if you want to execute it no matter if you get an error, then write like this.</p>
<pre>
next(<span class="Identifier">function</span> () {
<span class="Comment"> // something 1</span>
}).
next(<span class="Identifier">function</span> () {
<span class="Comment"> // asynchronous process</span>
<span class="Statement">throw</span> <span class="Constant">&quot;error!&quot;</span>;
}).
error(<span class="Identifier">function</span> (e) {
alert(e);
}).
next(<span class="Identifier">function</span> () {
<span class="Comment"> // something 2 (executed since the exception would already be handled)</span>
}).
error(<span class="Identifier">function</span> (e) {
alert(e);
});
</pre>
<p>You can slide an error handling in the middle. The process after the error() is always executed unless you get another exception in the error() process.</p>
</section>
<section>
<h1>Chain</h1>
<p>If a Deferred instance is returned in a function given to a Deferred process, it waits for the returned Deferred.</p>
<pre>
next(<span class="Identifier">function</span> () {
alert(<span class="Constant">&quot;Hello!&quot;</span>);
<span class="Statement">return</span> wait(<span class="Constant">5</span>);
}).
next(<span class="Identifier">function</span> () {
alert(<span class="Constant">&quot;World!&quot;</span>);
});
</pre>
<p>In the code above, wait() is a function to return a Deferred that "waits for 5 seconds". In this case, the following process waits for the returned Deferred to execute. You can return any other function than wait, which returns a Deferred.</p>
<p>next() also returns a Deferred instance, so you can write as this.</p>
<pre>
next(<span class="Identifier">function</span> () {
alert(<span class="Constant">1</span>);
<span class="Statement">return</span> next(<span class="Identifier">function</span> () {
alert(<span class="Constant">2</span>);
}).
next(<span class="Identifier">function</span> () {
alert(<span class="Constant">3</span>);
});
}).
next(<span class="Identifier">function</span> () {
alert(<span class="Constant">4</span>);
});
</pre>
<p>This is executed in the numerical order.</p>
</section>
<section>
<h1>"Deferredize" a Function</h1>
<p>When you use JSDeferred, you will often find it useful to define a custom function return a Deferred, rather than having it take a callback in a normal fashion.
It's very easy to do it indeed. As an example of XMLHttpRequest, I'm going to define the http.get which we saw a few times above.</p>
<pre>
http = {}
http.get = <span class="Identifier">function</span> (uri) {
<span class="Type">var</span> deferred = <span class="Statement">new</span> Deferred();
<span class="Type">var</span> xhr = <span class="Statement">new</span> XMLHttpRequest();
xhr.onreadystatechange = <span class="Identifier">function</span> () {
<span class="Statement">if</span> (xhr.readyState == <span class="Constant">4</span>) {
<span class="Statement">if</span> (xhr.status == <span class="Constant">200</span>) {
deferred.call(xhr);
} <span class="Statement">else</span> {
deferred.fail(xhr);
}
}
};
deferred.canceller = <span class="Identifier">function</span> () { xhr.abort() };
<span class="Statement">return</span> deferred;
}
</pre>
<p>Create a Deferred instance with "new Deferred()", and call its "call()" method within an asynchronous callback.
It will execute processes associated to the callback chain of the Deferred.</p>
<p>Similarly, calling the "fail()" method fires an error. If you want to catch the exception using ".error()", you need to call the "fail()" appropreately.</p>
<p>I also defined the canceller. It's executed when the cancel() method of the Deferred instance is called.
Normally you don't use it much, but you can remember that it exists.</p>
<h2>Coding an Asynchronous Process</h2>
<p>When you write a code that depends on Deferred, it is convenient if you write all asynchronous processes as functions to return a Deferred instance.
Even if you don't need any process to follow it, it makes you easy to put it into a Deferred chain.</p>
<p>If you are writing a general library, it may be good to first write functions to take callback functions, and then create a function to Deferredize them.</p>
</section>
<section>
<h1>Dividing a Heavy Process</h1>
<aside class="note">
<h1>"Speeding Up" JavaScript</h1>
<p>When you say "Speed Up" JavaScript, it is very important to reduce user's stress rather than speeding up of the process.</p>
<p>No matter how fast the process is, if it blocks the UI thread for a long time it gives a big stress to the user.
In JavaScript, <strong>blocking time of UI thread</strong> is more important than the overall time of execution.</p>
<p>JSDeferred makes it easy to divide a heavy process, for example, using loop() and execute it in batches.
When you create an application that is fast in the sense of total execution time, but blocks browsers' scrolling (UI), being able to fix it quickly is important in web application development.</p>
</aside>
<p>Handling DOM a large number of times with JavaScript can be very heavy for some browsers.
As a result, browser's UI process such as scrolling gets stuck.
This can be very stressful for the users, so you should avoid such a thing to happen.</p>
<p>Of course, it is essential to write an efficient code, but the DOM handling could be inevitable.
In that case, dividing a process and running asynchronously can make the browser UI smooth.</p>
<p>The loop() function of JSDeferred can give back control to the browser after each loop.</p>
<pre>
loop(<span class="Constant">1000</span>, <span class="Identifier">function</span> (n) {
<span class="Comment"> // heavy process</span>
});
</pre>
<p>Imagine writing this without JSDeferred…… I wouldn't dare to do it.</p>
<h2>Automatically Divide a Long Loop</h2>
<p>loop() function is effective when each loop is a heavy process. However, when a single loop is not so heavy but the number of iteration is numerous, it's not efficient.
Here I define a function called repeat()</p>
<pre>
<span class="Identifier">function</span> repeat (n, f) {
<span class="Type">var</span> i = <span class="Constant">0</span>, end = {}, ret = <span class="Type">null</span>;
<span class="Statement">return</span> Deferred.next(<span class="Identifier">function</span> () {
<span class="Type">var</span> t = (<span class="Statement">new</span> <span class="Special">Date</span>()).getTime();
<span class="Statement">divide</span>: {
<span class="Statement">do</span> {
<span class="Statement">if</span> (i &gt;= n) <span class="Statement">break</span> divide;
ret = f(i++);
} <span class="Statement">while</span> ((<span class="Statement">new</span> <span class="Special">Date</span>()).getTime() - t &lt; <span class="Constant">20</span>);
<span class="Statement">return</span> Deferred.call(arguments.callee);
}
});
}
</pre>
<p>This is a loop that is automatically divided up every 20 msec.
Unlike the ordinary loop(), it cannot wait even if a Deferred instance is returned in a loop.</p>
</section>
</section><!-- Tutorial -->
<section class="toplevel grid-12" id="cookbook">
<h1>Cookbook</h1>
<section>
<h1>Basic Chain</h1>
<pre class="runnable">
next(<span class="Identifier">function</span> () {
console.log(<span class="Constant">&quot;start&quot;</span>);
}).
next(<span class="Identifier">function</span> () {
<span class="Identifier">function</span> pow (x, n) {
<span class="Identifier">function</span> _pow (n, r) {
console.log([n, r]);
<span class="Statement">if</span> (n == <span class="Constant">0</span>) <span class="Statement">return</span> r;
<span class="Statement">return</span> call(_pow, n - <span class="Constant">1</span>, x * r);
}
<span class="Statement">return</span> call(_pow, n, <span class="Constant">1</span>);
}
<span class="Statement">return</span> call(pow, <span class="Constant">2</span>, <span class="Constant">10</span>);
}).
next(<span class="Identifier">function</span> (r) {
console.log([r, <span class="Constant">&quot;end&quot;</span>]);
}).
error(<span class="Identifier">function</span> (e) {
alert(e);
})
</pre>
</section>
<section>
<h1>Basic Loop</h1>
<pre class="runnable">
loop(<span class="Constant">10</span>, <span class="Identifier">function</span> (i) {
console.log(i)
});
</pre>
</section>
<section>
<h1>Deferred Ajax</h1>
<pre class="runnable">
$.get(<span class="Constant">&quot;README.markdown&quot;</span>).next(<span class="Identifier">function</span> (data) {
console.log(data);
});
</pre>
</section>
<section>
<h1>Parallel Deferred</h1>
<p>Array of deferreds:</p>
<pre class="runnable">
console.log(<span class="Constant">&quot;start. gathering data.&quot;</span>);
parallel([$.get(<span class="Constant">&quot;README.markdown&quot;</span>), $.get(<span class="Constant">&quot;ChangeLog&quot;</span>)]).
next(<span class="Identifier">function</span> (values) {
<span class="Type">var</span> lengths = $.map(values, <span class="Identifier">function</span> (i) { <span class="Statement">return</span> i.length });
console.log(lengths.join(<span class="Constant">&quot;, &quot;</span>));
});
</pre>
<p>Named parallel deferreds:</p>
<pre class="runnable">
console.log(<span class="Constant">&quot;start. gathering data.&quot;</span>);
parallel({<span class="Statement">html</span>: $.get(<span class="Constant">&quot;README.markdown&quot;</span>), <span class="Statement">js</span>: $.get(<span class="Constant">&quot;ChangeLog&quot;</span>)}).
next(<span class="Identifier">function</span> (values) {
console.log([<span class="Constant">&quot;html=&quot;</span>, values.html.length, <span class="Constant">&quot; js=&quot;</span>, values.js.length].join(<span class="Constant">&quot;&quot;</span>));
});
</pre>
<p>Arbitrary number of deferreds in array:</p>
<pre class="runnable">
console.log(<span class="Constant">&quot;start. wait 3 sec.&quot;</span>);
<span class="Type">var</span> list = [];
<span class="Type">var</span> printAndReturn = <span class="Identifier">function</span> (i) { console.log(i+<span class="Constant">&quot;msec elapsed&quot;</span>); <span class="Statement">return</span> i; };
list.push(wait(<span class="Constant">0</span>).next(printAndReturn));
list.push(wait(<span class="Constant">1</span>).next(printAndReturn));
list.push(wait(<span class="Constant">2</span>).next(printAndReturn));
list.push(wait(<span class="Constant">3</span>).next(printAndReturn));
parallel(list).next(<span class="Identifier">function</span> (values) {
console.log(<span class="Constant">&quot;Completed. values: &quot;</span>+values.join(<span class="Constant">&quot;, &quot;</span>));
});
</pre>
</section>
<section>
<h1>Workers</h1>
<pre class="runnable">
<span class="Type">var</span> queue = [<span class="Constant">1</span>, <span class="Constant">2</span>, <span class="Constant">3</span>, <span class="Constant">4</span>, <span class="Constant">5</span>, <span class="Constant">6</span>, <span class="Constant">7</span>, <span class="Constant">8</span>, <span class="Constant">9</span>, <span class="Constant">10</span>];
<span class="Type">var</span> workers = <span class="Statement">new</span> <span class="Special">Array</span>(<span class="Constant">2</span>);
<span class="Type">var</span> work = <span class="Identifier">function</span> (job) {
console.log(<span class="Constant">'working... '</span> + job);
<span class="Statement">return</span> wait(<span class="Special">Math</span>.random() * <span class="Constant">4</span>);
};
<span class="Statement">for</span> (<span class="Type">var</span> i = <span class="Constant">0</span>, len = workers.length; i &lt; len; i++) {
workers[i] = next(<span class="Identifier">function</span> me () {
<span class="Type">var</span> job = queue.shift();
<span class="Statement">if</span> (!job) <span class="Statement">return</span>;
console.log(<span class="Constant">&quot;start worker: &quot;</span> + job);
<span class="Statement">return</span> next(<span class="Identifier">function</span> () { <span class="Statement">return</span> job }).
next(work).
next(me);
}).
error(<span class="Identifier">function</span> (e) {
alert(e);
});
}
parallel(workers).next(<span class="Identifier">function</span> () {
console.log(<span class="Constant">'all done!'</span>);
});
</pre>
</section>
<section>
<h1>Divided Loop</h1>
<pre class="runnable">
next(<span class="Identifier">function</span> () {
<span class="Type">var</span> sum = <span class="Constant">0</span>;
<span class="Statement">return</span> loop({<span class="Statement">end</span>:100000, <span class="Statement">step</span>:1000}, <span class="Identifier">function</span> (n, o) {
console.log([<span class="Constant">&quot;Processing divided loop:n=&quot;</span>, n, <span class="Constant">&quot;, sum=&quot;</span>, sum, <span class="Constant">&quot; last?=&quot;</span>, o.last].join(<span class="Constant">&quot;&quot;</span>));
<span class="Statement">for</span> (<span class="Type">var</span> i = <span class="Constant">0</span>; i &lt; o.step; i++) {
<span class="Comment"> // console.log(i + n);</span>
sum += i + n;
}
console.log([<span class="Constant">&quot;sum=&quot;</span>, sum].join(<span class="Constant">&quot;&quot;</span>));
<span class="Statement">return</span> sum;
});
}).
next(<span class="Identifier">function</span> (e) {
console.log(<span class="Constant">&quot;Result:&quot;</span>+e);
console.log(<span class="Constant">&quot;end&quot;</span>);
}).
error(<span class="Identifier">function</span> (e) {
console.log(e);
});
</pre>
<pre class="runnable">
loop({<span class="Statement">begin</span>: <span class="Constant">1</span>, <span class="Statement">end</span>:100, <span class="Statement">step</span>:10}, <span class="Identifier">function</span> (n, o) {
console.log([<span class="Constant">&quot;Processing divided loop:n=&quot;</span>, n, <span class="Constant">&quot; last?=&quot;</span>, o.last].join(<span class="Constant">&quot;&quot;</span>));
<span class="Statement">for</span> (<span class="Type">var</span> i = <span class="Constant">0</span>; i &lt; o.step; i++) {
<span class="Type">var</span> j = n + i;
console.log(j);
}
});
</pre>
</section>
<section>
<h1>Auto Divided Loop</h1>
<pre class="runnable">
Deferred.repeat(<span class="Constant">100</span>, <span class="Identifier">function</span> (n, o) {
console.log(n);
<span class="Statement">for</span> (<span class="Type">var</span> i = <span class="Constant">0</span>; i &lt; <span class="Special">Math</span>.pow(n, <span class="Constant">2</span>); i++) {
<span class="Statement">for</span> (<span class="Type">var</span> j = n; j; j--);
}
});
</pre>
</section>
<section>
<h1>Pseudo Multi Thread</h1>
<pre class="runnable">
loop(<span class="Constant">10</span>, <span class="Identifier">function</span> (n) {
console.log(n);
<span class="Statement">return</span> wait(<span class="Constant">0.1</span>);
});
loop(<span class="Constant">10</span>, <span class="Identifier">function</span> (n) {
console.log(<span class="Special">String</span>.fromCharCode(<span class="Constant">97</span>+n));
<span class="Statement">return</span> wait(<span class="Constant">0.2</span>);
});
</pre>
</section>
<section>
<h1>Shorthand</h1>
<pre class="runnable">
console.log(<span class="Constant">0</span>);
loop(<span class="Constant">10</span>, <span class="Identifier">function</span> (n) {
console.log(n);
<span class="Statement">return</span> n;
}).
wait(<span class="Constant">1</span>).
loop(<span class="Constant">10</span>, <span class="Identifier">function</span> (n) {
<span class="Type">var</span> c = <span class="Special">String</span>.fromCharCode(<span class="Constant">97</span>+n);
console.log(c);
<span class="Statement">return</span> c;
}).
next(<span class="Identifier">function</span> (i) {
console.log(<span class="Constant">&quot;end&quot;</span>);
}).
error(<span class="Identifier">function</span> (e) {
alert(e);
});
</pre>
</section>
<section>
<h1>Delay Loop</h1>
<pre class="runnable">
loop(<span class="Constant">5</span>, <span class="Identifier">function</span> (i, o) {
console.log(i);
<span class="Statement">return</span> o.last? i : wait(<span class="Constant">1</span>);
}).
next(<span class="Identifier">function</span> (e) {
console.log(<span class="Constant">&quot;end [&quot;</span>+e+<span class="Constant">&quot;]&quot;</span>);
}).
error(<span class="Identifier">function</span> (e) {
console.log(e);
});
</pre>
<pre class="runnable">
next(<span class="Identifier">function</span> (i) {
<span class="Identifier">function</span> delayloop (i) {
console.log(i++);
<span class="Statement">if</span> (i &lt; <span class="Constant">5</span>) {
<span class="Statement">return</span> wait(<span class="Constant">1</span>).next(<span class="Identifier">function</span> () {
<span class="Statement">return</span> call(delayloop, i);
});
}
}
<span class="Statement">return</span> call(delayloop, <span class="Constant">0</span>);
}).
next(<span class="Identifier">function</span> (e) {
console.log(<span class="Constant">&quot;end&quot;</span>);
}).
error(<span class="Identifier">function</span> (e) {
console.log(e);
});
</pre>
</section>
<section>
<h1>Step Run (event handling)</h1>
<pre class="runnable" id="step-run">
<span class="Type">var</span> deferred = Deferred();
$(<span class="Constant">&quot;#step-run&quot;</span>).click(<span class="Identifier">function</span> () { deferred.call() });
loop(<span class="Constant">5</span>, <span class="Identifier">function</span> (i) {
console.log(<span class="Constant">&quot;running... &quot;</span> + i);
<span class="Statement">return</span> deferred;
}).
next(<span class="Identifier">function</span> () {
console.log(<span class="Constant">&quot;completed&quot;</span>);
});
</pre>
</section>
<section>
<h1>Brainfuck interpreter</h1>
<pre id="bfi-code">
<span class="Identifier">function</span> bfrun () {
<span class="Type">var</span> mem = $(<span class="Constant">&quot;&lt;pre/&gt;&quot;</span>), out = $(<span class="Constant">&quot;&lt;pre/&gt;&quot;</span>), button = $(<span class="Constant">&quot;#bfi-run&quot;</span>).hide();
<span class="Type">var</span> sinput = $(<span class="Constant">&quot;#bfi-source&quot;</span>).hide(), source = sinput.val();
<span class="Type">var</span> shtml = $(<span class="Constant">&quot;&lt;pre class='code'/&gt;&quot;</span>);
<span class="Type">var</span> selems = $.map(source.split(<span class="Constant">/</span><span class="Special">\n</span><span class="Constant">/</span>), <span class="Identifier">function</span> (l) {
<span class="Type">var</span> line = $(<span class="Constant">&quot;&lt;div class='line1'/&gt;&quot;</span>).appendTo(shtml);
<span class="Statement">return</span> $.map(l.split(<span class="Constant">&quot;&quot;</span>), <span class="Identifier">function</span> (c) {
<span class="Statement">return</span> $(<span class="Constant">&quot;&lt;span/&gt;&quot;</span>).append(c).appendTo(line);
})
});
sinput.before(mem).before(out).before(shtml);
<span class="Type">var</span> pi = {
<span class="Constant">&quot;+&quot;</span>: <span class="Identifier">function</span> (c) {
<span class="Statement">if</span> (!c.stack[<span class="Constant">0</span>]) <span class="Statement">return</span> c;
c.memory[c.pointer]++;
<span class="Statement">return</span> c;
},
<span class="Constant">&quot;-&quot;</span>: <span class="Identifier">function</span> (c) {
<span class="Statement">if</span> (!c.stack[<span class="Constant">0</span>]) <span class="Statement">return</span> c;
c.memory[c.pointer]--;
<span class="Statement">return</span> c;
},
<span class="Constant">&quot;&gt;&quot;</span>: <span class="Identifier">function</span> (c) {
<span class="Statement">if</span> (!c.stack[<span class="Constant">0</span>]) <span class="Statement">return</span> c;
c.pointer++;
c.memory[c.pointer] = (c.memory[c.pointer] || <span class="Constant">0</span>);
<span class="Statement">return</span> c;
},
<span class="Constant">&quot;&lt;&quot;</span>: <span class="Identifier">function</span> (c) {
<span class="Statement">if</span> (!c.stack[<span class="Constant">0</span>]) <span class="Statement">return</span> c;
c.pointer--;
c.memory[c.pointer] = (c.memory[c.pointer] || <span class="Constant">0</span>);
<span class="Statement">return</span> c;
},
<span class="Constant">&quot;.&quot;</span>: <span class="Identifier">function</span> (c) {
<span class="Statement">if</span> (!c.stack[<span class="Constant">0</span>]) <span class="Statement">return</span> c;
c.output.push(<span class="Special">String</span>.fromCharCode(c.memory[c.pointer]));
<span class="Statement">return</span> c;
},
<span class="Constant">&quot;,&quot;</span>: <span class="Identifier">function</span> (c) {
<span class="Statement">if</span> (!c.stack[<span class="Constant">0</span>]) <span class="Statement">return</span> c;
c.memory[c.pointer] = <span class="Constant">&quot;t&quot;</span>;
<span class="Statement">return</span> c;
},
<span class="Constant">&quot;[&quot;</span>: <span class="Identifier">function</span> (c) {
<span class="Statement">if</span> (c.memory[c.pointer] == <span class="Constant">0</span>) {
c.stack.unshift(<span class="Constant">false</span>);
} <span class="Statement">else</span> {
c.stack.unshift(c.pos - <span class="Constant">1</span>);
}
<span class="Statement">return</span> c;
},
<span class="Constant">&quot;]&quot;</span>: <span class="Identifier">function</span> (c) {
<span class="Type">var</span> s = c.stack.shift();
<span class="Statement">if</span> (s) c.pos = s;
<span class="Statement">return</span> c;
}
};
next(<span class="Identifier">function</span> () {
<span class="Statement">return</span> {
<span class="Statement">pos</span> : <span class="Constant">0</span>,
<span class="Statement">pointer</span> : <span class="Constant">0</span>,
<span class="Statement">memory</span> : [<span class="Constant">0</span>],
<span class="Statement">stack</span> : [<span class="Constant">true</span>],
<span class="Statement">output</span> : [],
<span class="Statement">source</span> : source
};
}).
next(<span class="Identifier">function</span> (c) {
<span class="Statement">if</span> (selems[c.pos - 1]) selems[c.pos - 1].removeClass(<span class="Constant">&quot;em&quot;</span>);
<span class="Type">var</span> chr, fun;
<span class="Statement">do</span> {
chr = c.source.charAt(c.pos);
fun = pi[chr];
c.pos++;
} <span class="Statement">while</span> (chr.match(<span class="Constant">/[^&lt;&gt;,.</span><span class="Special">\[\]</span><span class="Constant">+-]/</span>));
<span class="Type">var</span> m = c.memory.concat();
m.splice(c.pointer, <span class="Constant">1</span>, <span class="Constant">&quot;*&quot;</span>+(c.memory[c.pointer] || <span class="Constant">0</span>));
mem.text(m.join(<span class="Constant">&quot;, &quot;</span>));
out.text(c.output.join(<span class="Constant">&quot;&quot;</span>));
<span class="Statement">if</span> (<span class="Statement">typeof</span> fun == <span class="Constant">&quot;function&quot;</span>) {
c = fun(c);
<span class="Statement">if</span> (c.pos &lt; c.source.length) {
<span class="Statement">if</span> (selems[c.pos - 1]) selems[c.pos - 1].addClass(<span class="Constant">&quot;em&quot;</span>);
<span class="Statement">return</span> call(arguments.callee, c);
} <span class="Statement">else</span> {
<span class="Statement">return</span> c;
}
} <span class="Statement">else</span> {
<span class="Statement">return</span> c;
}
}).
next(<span class="Identifier">function</span> (c) {
<span class="Type">var</span> reset = $(<span class="Constant">&quot;&lt;input type='button' value='Reset' class='button'/>&quot;</span>)
sinput.after(reset);
reset.click(<span class="Identifier">function</span> () {
sinput.show();
button.show();
mem.remove();
out.remove();
shtml.remove();
reset.remove();
});
console.log(<span class="Constant">&quot;end&quot;</span>);
}).
error(<span class="Identifier">function</span> (e) {
alert(e);
});
}
</pre>
<script type="text/javascript">eval($("#bfi-code").text());</script>
<textarea id="bfi-source" cols="50" rows="5">
+++++++++[->+++++>++++++++>+++++++++++<<<]>
>.>++.+++++++..+++.<<-.[->-<]>++++.>+++++++
+.--------.+++.------.--------.<+.
</textarea>
<input id="bfi-run" type="button" onclick="bfrun();" value="Run" class="button"/>
</section>
<section>
<h1>Slideshow</h1>
<ul>
<li>Load external resources</li>
<li>Preload next image</li>
<li>Infinite loop</li>
</ul>
<img id="slideshow-image"/>
<pre class="runnable">
<span class="Type">var</span> img = $(<span class="Constant">'img#slideshow-image'</span>);
<span class="Identifier">function</span> loadImage (url) {
console.log(<span class="Constant">"loadImage: "</span> + url);
<span class="Type">var</span> d = <span class="Statement">new</span> Deferred();
<span class="Type">var</span> img = <span class="Statement">new</span> Image();
img.src = url;
img.onload = <span class="Identifier">function</span> () {
d.call();
};
<span class="Statement">return</span> d;
}
<span class="Identifier">function</span> showImage (url) {
<span class="Type">var</span> d = <span class="Statement">new</span> Deferred();
img.one(<span class="Constant">'load'</span>, <span class="Identifier">function</span> () {
d.call();
});
img.attr(<span class="Constant">'src'</span>, url);
<span class="Statement">return</span> d;
}
console.log(<span class="Constant">"Loading photo list..."</span>);
$.ajax({
<span class="Statement">url</span> : <span class="Constant">'<a href="http://picasaweb.google.com/data/feed/base/user/cho101101?alt=json&amp;kind=photo&amp;hl=ja&amp;access=public&amp;callback=?">http://picasaweb.google.com/data/feed/base/user/cho101101?alt=json&amp;kind=photo&amp;hl=ja&amp;access=public&amp;callback=?</a>'</span>,
<span class="Statement">dataType</span>: <span class="Constant">'jsonp'</span>,
<span class="Statement">data</span> : {
<span class="Constant">'fields'</span> : <span class="Constant">'entry(title,link,content,media:group)'</span>,
<span class="Constant">'start-index'</span> : <span class="Constant">1</span>,
<span class="Constant">'max-results'</span> : <span class="Constant">3</span>,
<span class="Constant">'thumbsize'</span> : <span class="Constant">'144c'</span>
}
}).
next(<span class="Identifier">function</span> (data) {
console.log(<span class="Constant">"Loading photo list... done"</span>);
<span class="Type">var</span> n = <span class="Constant">0</span>;
img.attr(<span class="Constant">'src'</span>, data.feed.entry[<span class="Constant">0</span>].media$group.media$thumbnail[<span class="Constant">0</span>].url);
<span class="Statement">return</span> next(<span class="Identifier">function</span> () {
n = (n + <span class="Constant">1</span>) % data.feed.entry.length;
<span class="Type">var</span> entry = data.feed.entry[n];
<span class="Type">var</span> url = entry.media$group.media$thumbnail[<span class="Constant">0</span>].url;
<span class="Statement">return</span> parallel([
wait(<span class="Constant">3</span>),
loadImage(url) <span class="Comment">// preload</span>
]).
next(<span class="Identifier">function</span> () {
<span class="Statement">return</span> img.fadeTo(<span class="Constant">250</span>, <span class="Constant">0</span>).promise();
}).
next(<span class="Identifier">function</span> () {
<span class="Statement">return</span> showImage(url);
}).
next(<span class="Identifier">function</span> () {
<span class="Statement">return</span> img.fadeTo(<span class="Constant">250</span>, <span class="Constant">1</span>).promise();
}).
next(arguments.callee);
});
}).
error(<span class="Identifier">function</span> (e) {
alert(e);
});
</pre>
</section>
<section>
<h1>callcc</h1>
<p>like Scheme's callcc</p>
<pre class="runnable">
<span class="Identifier">function</span> callcc (fun) {
<span class="Type">var</span> error = <span class="Statement">new</span> Deferred();
<span class="Statement">return</span> call(<span class="Identifier">function</span> () {
<span class="Comment"> // JSDeferred passes current Deferred Object to this.</span>
<span class="Type">var</span> ccdeferred = <span class="Type">this</span>;
<span class="Comment"> // Call with current continuation (calling Deferred.next)</span>
<span class="Statement">return</span> fun(<span class="Identifier">function</span> (a) { ccdeferred._next.call(a); <span class="Statement">throw</span> error });
}).
error(<span class="Identifier">function</span> (e) {
<span class="Comment"> // Current Deferred chain must be stopped</span>
<span class="Statement">if</span> (e === error) {
<span class="Statement">return</span> e;
} <span class="Statement">else</span> {
<span class="Statement">throw</span> e;
}
});
}
callcc(<span class="Identifier">function</span> (cont) {
<span class="Statement">return</span> <span class="Constant">10</span> * <span class="Constant">10</span> * cont(<span class="Constant">20</span>);
}).
next(<span class="Identifier">function</span> (val) {
console.log(<span class="Constant">&quot;callcc1 returns:&quot;</span> + val);
});
<span class="Comment">// should show &quot;callcc1 returns:20&quot;</span>
<span class="Type">var</span> cont;
<span class="Type">var</span> i = <span class="Constant">0</span>;
callcc(<span class="Identifier">function</span> (c) {
cont = c;
<span class="Statement">return</span> <span class="Constant">10</span>;
}).
next(<span class="Identifier">function</span> (val) {
console.log(<span class="Constant">&quot;callcc2 returns:&quot;</span> + val);
<span class="Statement">if</span> (!i++) cont(<span class="Constant">20</span>);
});
<span class="Comment">// should show &quot;callcc2 returns:10&quot;, &quot;callcc returns:20&quot;</span>
</pre>
<p>and Scheme's amb</p>
<pre class="runnable">
<span class="Identifier">function</span> callcc (fun) {
<span class="Type">var</span> error = <span class="Statement">new</span> Deferred();
<span class="Statement">return</span> call(<span class="Identifier">function</span> () {
<span class="Comment"> // JSDeferred passes current Deferred Object to this.</span>
<span class="Type">var</span> ccdeferred = <span class="Type">this</span>;
<span class="Comment"> // Call with current continuation (calling Deferred.next)</span>
<span class="Statement">return</span> fun(<span class="Identifier">function</span> (a) { ccdeferred._next.call(a); <span class="Statement">throw</span> error });
}).
error(<span class="Identifier">function</span> (e) {
<span class="Comment"> // Current Deferred chain must be stopped</span>
<span class="Statement">if</span> (e === error) {
<span class="Statement">return</span> e;
} <span class="Statement">else</span> {
<span class="Statement">throw</span> e;
}
});
}
<span class="Comment">// <a href="http://www.sampou.org/scheme/t-y-scheme/t-y-scheme-Z-H-16.html#node_chap_14">http://www.sampou.org/scheme/t-y-scheme/t-y-scheme-Z-H-16.html#node_chap_14</a></span>
<span class="Comment">// Just port above.</span>
<span class="Identifier">function</span> amb () {
<span class="Type">var</span> alts = arguments;
<span class="Type">var</span> prevAmbFail = amb.ambFail;
<span class="Statement">return</span> callcc(<span class="Identifier">function</span> (sk) {
<span class="Statement">return</span> loop(alts.length, <span class="Identifier">function</span> (i) {
<span class="Type">var</span> alt = alts[i];
<span class="Statement">return</span> callcc(<span class="Identifier">function</span> (fk) {
amb.ambFail = <span class="Identifier">function</span> () {
amb.ambFail = prevAmbFail;
<span class="Statement">return</span> fk(<span class="Constant">&quot;fail&quot;</span>);
};
<span class="Statement">return</span> sk(alt);
});
}).
next(prevAmbFail);
});
}
amb.ambFail = <span class="Identifier">function</span> () { <span class="Statement">throw</span> <span class="Constant">&quot;amb tree exhausted&quot;</span> };
<span class="Comment">// Utility function</span>
<span class="Identifier">function</span> amb1 (ambvars) {
<span class="Type">var</span> f = wait(<span class="Constant">0</span>);
<span class="Type">var</span> vars = {};
<span class="Statement">for</span> (<span class="Type">var</span> k <span class="Statement">in</span> ambvars) <span class="Statement">if</span> (ambvars.hasOwnProperty(k)) (<span class="Identifier">function</span> (name, val) {
console.log(name);
f = f.next(<span class="Identifier">function</span> () {
<span class="Statement">return</span> amb.apply(<span class="Type">this</span>, val).next(<span class="Identifier">function</span> (i) {
vars[name] = i;
<span class="Statement">return</span> vars;
});
});
})(k, ambvars[k]);
<span class="Statement">return</span> f;
}
<span class="Identifier">function</span> assert (cond) {
<span class="Statement">if</span> (!cond) <span class="Statement">throw</span> amb();
}
<span class="Comment">// <a href="http://mayokara.info/note/view/251">http://mayokara.info/note/view/251</a></span>
<span class="Special">Array</span>.prototype.uniq = <span class="Identifier">function</span>(){
<span class="Statement">for</span> (<span class="Type">var</span> i = <span class="Constant">0</span>,l = <span class="Type">this</span>.length; i &lt; l; i++) {
<span class="Statement">if</span> (<span class="Type">this</span>.indexOf(<span class="Type">this</span>[i]) &lt; i) {
<span class="Type">this</span>.splice(i--, l-- &amp;&amp; <span class="Constant">1</span>);
}
}
<span class="Statement">return</span> <span class="Type">this</span>;
};
amb1({
<span class="Statement">baker</span> : [<span class="Constant">1</span>, <span class="Constant">2</span>, <span class="Constant">3</span>, <span class="Constant">4</span>, <span class="Constant">5</span>],
<span class="Statement">cooper</span> : [<span class="Constant">1</span>, <span class="Constant">2</span>, <span class="Constant">3</span>, <span class="Constant">4</span>, <span class="Constant">5</span>],
<span class="Statement">fletcher</span> : [<span class="Constant">1</span>, <span class="Constant">2</span>, <span class="Constant">3</span>, <span class="Constant">4</span>, <span class="Constant">5</span>],
<span class="Statement">miller</span> : [<span class="Constant">1</span>, <span class="Constant">2</span>, <span class="Constant">3</span>, <span class="Constant">4</span>, <span class="Constant">5</span>],
<span class="Statement">smith</span> : [<span class="Constant">1</span>, <span class="Constant">2</span>, <span class="Constant">3</span>, <span class="Constant">4</span>, <span class="Constant">5</span>]
}).
next(<span class="Identifier">function</span> (vars) { <span class="Statement">with</span> (vars) {
console.log(vars);
<span class="Comment"> // 簡易 distinct</span>
assert([baker, cooper, fletcher, miller, smith].uniq().length == <span class="Constant">5</span>);
console.log(<span class="Constant">&quot;distinct passed&quot;</span>);
assert(baker != <span class="Constant">5</span>);
assert(cooper != <span class="Constant">1</span>);
assert(fletcher != <span class="Constant">1</span> &amp;&amp; fletcher != <span class="Constant">5</span>);
assert(miller &gt; cooper);
assert(<span class="Special">Math</span>.abs(smith - fletcher) != <span class="Constant">1</span>);
assert(<span class="Special">Math</span>.abs(fletcher - cooper) != <span class="Constant">1</span>);
<span class="Statement">return</span> vars;
} }).
next(<span class="Identifier">function</span> (vars) { <span class="Statement">with</span> (vars) {
console.log(<span class="Constant">&quot;solved&quot;</span>);
console.log(vars);
alert(uneval(vars));
} }).
error(<span class="Identifier">function</span> (e) {
alert(e)
});
</pre>
</section>
</section><!-- Cookbook -->
<section class="toplevel grid-12" id="behavior">
<h1>Behavior</h1>
<aside class="note" style="float: none; margin: 20px 0">
<p>This sections use some words as following meanings.</p>
<dl>
<dt>Chain</dt>
<dd>a sequence of processes.</dd>
<dt>Child</dt>
<dd>the Deferred which returns from a callback.</dd>
<dt>Deferred#foobar</dt>
<dd>Deferred.prototype.foobar</dd>
</dl>
</aside>
<section>
<h1>Deferred structure and chain structure</h1>
<figure><img src="http://f.hatena.ne.jp/images/fotolife/c/cho45/20071208/20071208021643.png" alt="Deferred structure"/></figure>
<p>A Deferred object has only one callback as its process. Deferred object packages a process (function) as callback and has reference to next Deferred (this is thought like continuation).</p>
<p>Example for understanding Deferred structure.</p>
<pre>
<span class="Type">var</span> d1 = <span class="Statement">new</span> Deferred();
d1.callback.ok = <span class="Identifier">function</span> () {
alert(<span class="Constant">&quot;1&quot;</span>);
};
<span class="Type">var</span> d2 = <span class="Statement">new</span> Deferred();
d2.callback.ok = <span class="Identifier">function</span> () {
alert(<span class="Constant">&quot;2&quot;</span>);
};
<span class="Comment">// Set d2 as continuation of d1.</span>
d1._next = d2;
<span class="Comment">// Invoke the chain.</span>
d1.call();
</pre>
<p>And example for usual use.</p>
<pre>
next(<span class="Identifier">function</span> () { <span class="Comment">// this `next` is global function</span>
alert(<span class="Constant">&quot;1&quot;</span>);
}).
next(<span class="Identifier">function</span> () { <span class="Comment">// this `next` is Deferred#next</span>
alert(<span class="Constant">&quot;2&quot;</span>);
}).
next(<span class="Identifier">function</span> () {
alert(<span class="Constant">&quot;3&quot;</span>);
});</pre>
<p>Deferred#next creates new Deferred, sets the passed functions to process of it, sets it as continuation of `this` and returns it.</p>
<p>This structure makes easy to chain child Deferreds.</p>
<pre>
next(<span class="Identifier">function</span> () {
alert(<span class="Constant">&quot;1&quot;</span>);
}).
next(<span class="Identifier">function</span> () {
alert(<span class="Constant">&quot;2&quot;</span>);
<span class="Comment"> // child Deferred</span>
<span class="Statement">return</span> next(<span class="Identifier">function</span> () {
alert(<span class="Constant">&quot;3&quot;</span>);
});
}).
next(<span class="Identifier">function</span> () {
alert(<span class="Constant">&quot;4&quot;</span>);
});</pre>
<figure><img src="http://f.hatena.ne.jp/images/fotolife/c/cho45/20071207/20071207014817.png" alt="Chain child deferred"/></figure>
<p>When the callback returns Deferred, the Deferred calling the callback only sets its continuation (`_next`) to returned Deferred's continuation.</p>
<pre>
next(<span class="Identifier">function</span> () {
alert(<span class="Constant">&quot;1&quot;</span>);
}).
next(<span class="Identifier">function</span> () {
alert(<span class="Constant">&quot;2&quot;</span>);
<span class="Type">var</span> d = next(<span class="Identifier">function</span> () {
alert(<span class="Constant">&quot;3&quot;</span>);
});
d._next = <span class="Type">this</span>._next;
<span class="Type">this</span>.cancel();
}).
next(<span class="Identifier">function</span> () {
alert(<span class="Constant">&quot;4&quot;</span>);
});</pre>
<p>After the process, above code is same as following:</p>
<pre>
next(<span class="Identifier">function</span> () {
alert(<span class="Constant">&quot;1&quot;</span>);
}).
next(<span class="Identifier">function</span> () {
alert(<span class="Constant">&quot;2&quot;</span>);
next(<span class="Identifier">function</span> () {
alert(<span class="Constant">&quot;3&quot;</span>);
}).
next(<span class="Identifier">function</span> () {
alert(<span class="Constant">&quot;4&quot;</span>);
});
});</pre>
</section>
<section>
<h1>Error processing and recovering</h1>
<p>A Deferred has also error-back for error processing. Let's just see an example (this is from test):</p>
<pre>
next(<span class="Identifier">function</span> () {
<span class="Statement">throw</span> <span class="Constant">&quot;Error&quot;</span>;
}).
error(<span class="Identifier">function</span> (e) {
expect(<span class="Constant">&quot;Errorback called&quot;</span>, <span class="Constant">&quot;Error&quot;</span>, e);
<span class="Statement">return</span> e; <span class="Comment">// recovering error</span>
}).
next(<span class="Identifier">function</span> (e) {
expect(<span class="Constant">&quot;Callback called&quot;</span>, <span class="Constant">&quot;Error&quot;</span>, e);
<span class="Statement">throw</span> <span class="Constant">&quot;Error2&quot;</span>;
}).
next(<span class="Identifier">function</span> (e) {
<span class="Comment"> // This process is not called because</span>
<span class="Comment"> // the error is not recovered.</span>
ng(<span class="Constant">&quot;Must not be called!!&quot;</span>);
}).
error(<span class="Identifier">function</span> (e) {
expect(<span class="Constant">&quot;Errorback called&quot;</span>, <span class="Constant">&quot;Error2&quot;</span>, e);
});</pre>
<p>The error thrown in callback is propagated by error-back chain. If the error-back returns normal value, the error is considered as recovery, and the callback chain continues.</p>
</section>
<section>
<h1>Different-origin Deferred instances</h1>
<p>JSDeferred can be used in inter-environment which is independent respectively like browser extension
because JSDeferred determines a self-class identity by instance id.</p>
</section>
</section><!-- Behavior -->
<section class="toplevel grid-12" id="development">
<h1>Development</h1>
<div class="line">
<section id="repository" class="grid-6">
<h1>Repository</h1>
<p>JSDeferred is hosted on github.</p>
<pre>
<span class="Type">$</span> git clone git://github.com/cho45/jsdeferred.git
</pre>
</section>
<section id="test" class="grid-6">
<h1>Test</h1>
<p><a href="test.html">Browser Tests</a> or CUI tests:</p>
<pre>
<span class="Type">$</span> node test-node.js <a href="http://travis-ci.org/#!/cho45/jsdeferred"><img src="https://secure.travis-ci.org/cho45/jsdeferred.png?branch=master" style="vertical-align: middle"/></a>
or
<span class="Type">$</span> rhino test-rhino.js
or
<span class="Type">$</span> phantomjs test-phantomjs.js
</pre>
</section>
</div>
<div class="line">
<section class="grid-6">
<h1>Active Issues</h1>
<script src="http://cdn.washer-in-the-rye.com/githubapi.js" data-api="repos/cho45/jsdeferred/issues?sort=updated;state=open;direction=desc">
<div class="issues">
<% for (var i = 0, it; (it = data[i]) && i < 5; i++) { %>
<div class="issue <%= it.state %>">
<span class="number">#<%= it.number %></span>
<a href="<%= it.html_url %>" class="title"><%= it.title %></a>
by
<span class="author"><img src="<%= it.user.avatar_url %>" width="16" height="16"/><%= it.user.login %></span>
</div>
<% } %>
</div>
</script>
</section>
<section class="grid-6">
<h1>Closed Issues</h1>
<script src="http://cdn.washer-in-the-rye.com/githubapi.js" data-api="repos/cho45/jsdeferred/issues?sort=updated;state=closed;direction=desc">
<div class="issues">
<% for (var i = 0, it; (it = data[i]) && i < 5; i++) { %>
<div class="issue <%= it.state %>">
<span class="number">#<%= it.number %></span>
<a href="<%= it.html_url %>" class="title"><%= it.title %></a>
by
<span class="author"><img src="<%= it.user.avatar_url %>" width="16" height="16"/><%= it.user.login %></span>
</div>
<% } %>
</div>
</script>
</section>
</div>
<section>
<h1>Contributors</h1>
<script src="http://cdn.washer-in-the-rye.com/githubapi.js" data-api="repos/cho45/jsdeferred/contributors?">
<div class="contributors">
<% for (var i = 0, it; (it = data[i]); i++) { %>
<a href="http://github.com/<%= it.login %>" class="contributor">
<img src="<%= it.avatar_url %>" width="24" height="24"/>
<%= it.login %>
</a>
<% } %>
</div>
</script>
</section>
</section><!-- Development -->
</div>
</div>
</div>
<footer id="global-footer">
<address>Copyright &copy; <a href="http://www.lowreal.net/">cho45</a> 2007-2012, License under <a href="https://raw.github.com/cho45/jsdeferred/master/jsdeferred.js">MIT</a></address>
</footer>
<a href="https://github.com/cho45/jsdeferred"><img style="position: absolute; top: 0; right: 0; border: 0;" src="https://s3.amazonaws.com/github/ribbons/forkme_right_darkblue_121621.png" alt="Fork me on GitHub"></a>
</div>
</body>
</html>
Jump to Line
Something went wrong with that request. Please try again.