208 lines
13 KiB
HTML
Executable file
208 lines
13 KiB
HTML
Executable file
<a href="http://github.com/angular/angular.js/tree/v1.2.0rc1/src/ng/q.js#L3" class="view-source btn btn-action"><i class="icon-zoom-in"> </i> View source</a><a href="http://github.com/angular/angular.js/edit/master/src/ng/q.js" class="improve-docs btn btn-primary"><i class="icon-edit"> </i> Improve this doc</a><h1><code ng:non-bindable="">$q</code>
|
||
<div><span class="hint">service in module <code ng:non-bindable="">ng</code>
|
||
</span>
|
||
</div>
|
||
</h1>
|
||
<div><h2 id="Description">Description</h2>
|
||
<div class="description"><div class="ng-q-page"><p>A promise/deferred implementation inspired by <a href="https://github.com/kriskowal/q">Kris Kowal's Q</a>.</p>
|
||
<p><a href="http://wiki.commonjs.org/wiki/Promises">The CommonJS Promise proposal</a> describes a promise as an
|
||
interface for interacting with an object that represents the result of an action that is
|
||
performed asynchronously, and may or may not be finished at any given point in time.</p>
|
||
<p>From the perspective of dealing with error handling, deferred and promise APIs are to
|
||
asynchronous programming what <code>try</code>, <code>catch</code> and <code>throw</code> keywords are to synchronous programming.</p>
|
||
<pre class="prettyprint linenums">
|
||
// for the purpose of this example let's assume that variables `$q` and `scope` are
|
||
// available in the current lexical scope (they could have been injected or passed in).
|
||
|
||
function asyncGreet(name) {
|
||
var deferred = $q.defer();
|
||
|
||
setTimeout(function() {
|
||
// since this fn executes async in a future turn of the event loop, we need to wrap
|
||
// our code into an $apply call so that the model changes are properly observed.
|
||
scope.$apply(function() {
|
||
deferred.notify('About to greet ' + name + '.');
|
||
|
||
if (okToGreet(name)) {
|
||
deferred.resolve('Hello, ' + name + '!');
|
||
} else {
|
||
deferred.reject('Greeting ' + name + ' is not allowed.');
|
||
}
|
||
});
|
||
}, 1000);
|
||
|
||
return deferred.promise;
|
||
}
|
||
|
||
var promise = asyncGreet('Robin Hood');
|
||
promise.then(function(greeting) {
|
||
alert('Success: ' + greeting);
|
||
}, function(reason) {
|
||
alert('Failed: ' + reason);
|
||
}, function(update) {
|
||
alert('Got notification: ' + update);
|
||
});
|
||
</pre>
|
||
<p>At first it might not be obvious why this extra complexity is worth the trouble. The payoff
|
||
comes in the way of
|
||
<a href="https://github.com/kriskowal/uncommonjs/blob/master/promises/specification.md">guarantees that promise and deferred APIs make</a>.</p>
|
||
<p>Additionally the promise api allows for composition that is very hard to do with the
|
||
traditional callback (<a href="http://en.wikipedia.org/wiki/Continuation-passing_style">CPS</a>) approach.
|
||
For more on this please see the <a href="https://github.com/kriskowal/q">Q documentation</a> especially the
|
||
section on serial or parallel joining of promises.</p>
|
||
<h3>The Deferred API</h1>
|
||
<p>A new instance of deferred is constructed by calling <code>$q.defer()</code>.</p>
|
||
<p>The purpose of the deferred object is to expose the associated Promise instance as well as APIs
|
||
that can be used for signaling the successful or unsuccessful completion, as well as the status
|
||
of the task.</p>
|
||
<p><strong>Methods</strong></p>
|
||
<ul>
|
||
<li><code>resolve(value)</code> – resolves the derived promise with the <code>value</code>. If the value is a rejection
|
||
constructed via <code>$q.reject</code>, the promise will be rejected instead.</li>
|
||
<li><code>reject(reason)</code> – rejects the derived promise with the <code>reason</code>. This is equivalent to
|
||
resolving it with a rejection constructed via <code>$q.reject</code>.</li>
|
||
<li><code>notify(value)</code> - provides updates on the status of the promises execution. This may be called
|
||
multiple times before the promise is either resolved or rejected.</li>
|
||
</ul>
|
||
<p><strong>Properties</strong></p>
|
||
<ul>
|
||
<li>promise – <code>{Promise}</code> – promise object associated with this deferred.</li>
|
||
</ul>
|
||
<h1>The Promise API</h1>
|
||
<p>A new promise instance is created when a deferred instance is created and can be retrieved by
|
||
calling <code>deferred.promise</code>.</p>
|
||
<p>The purpose of the promise object is to allow for interested parties to get access to the result
|
||
of the deferred task when it completes.</p>
|
||
<p><strong>Methods</strong></p>
|
||
<ul>
|
||
<li><p><code>then(successCallback, errorCallback, notifyCallback)</code> – regardless of when the promise was or
|
||
will be resolved or rejected, <code>then</code> calls one of the success or error callbacks asynchronously
|
||
as soon as the result is available. The callbacks are called with a single argument: the result
|
||
or rejection reason. Additionally, the notify callback may be called zero or more times to
|
||
provide a progress indication, before the promise is resolved or rejected.</p>
|
||
<p>This method <em>returns a new promise</em> which is resolved or rejected via the return value of the
|
||
<code>successCallback</code>, <code>errorCallback</code>. It also notifies via the return value of the <code>notifyCallback</code>
|
||
method. The promise can not be resolved or rejected from the notifyCallback method.</p>
|
||
</li>
|
||
<li><p><code>catch(errorCallback)</code> – shorthand for <code>promise.then(null, errorCallback)</code></p>
|
||
</li>
|
||
<li><p><code>finally(callback)</code> – allows you to observe either the fulfillment or rejection of a promise,
|
||
but to do so without modifying the final value. This is useful to release resources or do some
|
||
clean-up that needs to be done whether the promise was rejected or resolved. See the <a href="https://github.com/kriskowal/q/wiki/API-Reference#promisefinallycallback">full
|
||
specification</a> for
|
||
more information.</p>
|
||
<p>Because <code>finally</code> is a reserved word in JavaScript and reserved keywords are not supported as
|
||
property names by ES3, you'll need to invoke the method like <code>promise['finally'](callback)</code> to
|
||
make your code IE8 compatible.</p>
|
||
</li>
|
||
</ul>
|
||
<h1>Chaining promises</h1>
|
||
<p>Because calling the <code>then</code> method of a promise returns a new derived promise, it is easily possible
|
||
to create a chain of promises:</p>
|
||
<pre class="prettyprint linenums">
|
||
promiseB = promiseA.then(function(result) {
|
||
return result + 1;
|
||
});
|
||
|
||
// promiseB will be resolved immediately after promiseA is resolved and its value
|
||
// will be the result of promiseA incremented by 1
|
||
</pre>
|
||
<p>It is possible to create chains of any length and since a promise can be resolved with another
|
||
promise (which will defer its resolution further), it is possible to pause/defer resolution of
|
||
the promises at any point in the chain. This makes it possible to implement powerful APIs like
|
||
$http's response interceptors.</p>
|
||
<h1>Differences between Kris Kowal's Q and $q</h1>
|
||
<p> There are three main differences:</p>
|
||
<ul>
|
||
<li>$q is integrated with the <a href="../../../../../index.htmlotScope.Scope"><code>ng.$rootScope.Scope</code></a> Scope model observation
|
||
mechanism in angular, which means faster propagation of resolution or rejection into your
|
||
models and avoiding unnecessary browser repaints, which would result in flickering UI.</li>
|
||
<li>$q promises are recognized by the templating engine in angular, which means that in templates
|
||
you can treat promises attached to a scope as if they were the resulting values.</li>
|
||
<li><p>Q has many more features than $q, but that comes at a cost of bytes. $q is tiny, but contains
|
||
all the important functionality needed for common async tasks.</p>
|
||
<h1>Testing</h3>
|
||
<pre class="prettyprint linenums">
|
||
it('should simulate promise', inject(function($q, $rootScope) {
|
||
var deferred = $q.defer();
|
||
var promise = deferred.promise;
|
||
var resolvedValue;
|
||
|
||
promise.then(function(value) { resolvedValue = value; });
|
||
expect(resolvedValue).toBeUndefined();
|
||
|
||
// Simulate resolving of promise
|
||
deferred.resolve(123);
|
||
// Note that the 'then' function does not get called synchronously.
|
||
// This is because we want the promise API to always be async, whether or not
|
||
// it got called synchronously or asynchronously.
|
||
expect(resolvedValue).toBeUndefined();
|
||
|
||
// Propagate promise resolution to 'then' functions using $apply().
|
||
$rootScope.$apply();
|
||
expect(resolvedValue).toEqual(123);
|
||
});
|
||
</pre>
|
||
</li>
|
||
</ul>
|
||
</div></div>
|
||
<h2 id="Dependencies">Dependencies</h2>
|
||
<ul class="dependencies"><li><code ng:non-bindable=""><a href="../../../../../index.htmlotScope">$rootScope</a></code>
|
||
</li>
|
||
</ul>
|
||
<div class="member method"><h2 id="Methods">Methods</h2>
|
||
<ul class="methods"><li><h3 id="all">all(promises)</h3>
|
||
<div class="all"><div class="ng-q-all-page"><p>Combines multiple promises into a single promise that is resolved when all of the input
|
||
promises are resolved.</p>
|
||
</div><h5 id="parameters">Parameters</h5><table class="variables-matrix table table-bordered table-striped"><thead><tr><th>Param</th><th>Type</th><th>Details</th></tr></thead><tbody><tr><td>promises</td><td><a href="" class="label type-hint type-hint-array">Array.<Promise></a><a href="" class="label type-hint type-hint-object">Object.<Promise></a></td><td><div class="ng-q-all-page"><p>An array or hash of promises.</p>
|
||
</div></td></tr></tbody></table><h5 id="returns">Returns</h5><table class="variables-matrix"><tr><td><a href="" class="label type-hint type-hint-promise">Promise</a></td><td><div class="ng-q-all-page"><p>Returns a single promise that will be resolved with an array/hash of values,
|
||
each value corresponding to the promise at the same index/key in the <code>promises</code> array/hash. If any of
|
||
the promises is resolved with a rejection, this resulting promise will be resolved with the
|
||
same rejection.</p>
|
||
</div></td></tr></table></div>
|
||
</li>
|
||
<li><h3 id="defer">defer()</h3>
|
||
<div class="defer"><div class="ng-q-defer-page"><p>Creates a <code>Deferred</code> object which represents a task which will finish in the future.</p>
|
||
</div><h5 id="returns">Returns</h5><table class="variables-matrix"><tr><td><a href="" class="label type-hint type-hint-deferred">Deferred</a></td><td><div class="ng-q-defer-page"><p>Returns a new instance of deferred.</p>
|
||
</div></td></tr></table></div>
|
||
</li>
|
||
<li><h3 id="reject">reject(reason)</h3>
|
||
<div class="reject"><div class="ng-q-reject-page"><p>Creates a promise that is resolved as rejected with the specified <code>reason</code>. This api should be
|
||
used to forward rejection in a chain of promises. If you are dealing with the last promise in
|
||
a promise chain, you don't need to worry about it.</p>
|
||
<p>When comparing deferreds/promises to the familiar behavior of try/catch/throw, think of
|
||
<code>reject</code> as the <code>throw</code> keyword in JavaScript. This also means that if you "catch" an error via
|
||
a promise error callback and you want to forward the error to the promise derived from the
|
||
current promise, you have to "rethrow" the error by returning a rejection constructed via
|
||
<code>reject</code>.</p>
|
||
<pre class="prettyprint linenums">
|
||
promiseB = promiseA.then(function(result) {
|
||
// success: do something and resolve promiseB
|
||
// with the old or a new result
|
||
return result;
|
||
}, function(reason) {
|
||
// error: handle the error if possible and
|
||
// resolve promiseB with newPromiseOrValue,
|
||
// otherwise forward the rejection to promiseB
|
||
if (canHandle(reason)) {
|
||
// handle the error and recover
|
||
return newPromiseOrValue;
|
||
}
|
||
return $q.reject(reason);
|
||
});
|
||
</pre>
|
||
</div><h5 id="parameters">Parameters</h5><table class="variables-matrix table table-bordered table-striped"><thead><tr><th>Param</th><th>Type</th><th>Details</th></tr></thead><tbody><tr><td>reason</td><td><a href="" class="label type-hint type-hint-object">*</a></td><td><div class="ng-q-reject-page"><p>Constant, message, exception or an object representing the rejection reason.</p>
|
||
</div></td></tr></tbody></table><h5 id="returns">Returns</h5><table class="variables-matrix"><tr><td><a href="" class="label type-hint type-hint-promise">Promise</a></td><td><div class="ng-q-reject-page"><p>Returns a promise that was already resolved as rejected with the <code>reason</code>.</p>
|
||
</div></td></tr></table></div>
|
||
</li>
|
||
<li><h3 id="when">when(value)</h3>
|
||
<div class="when"><div class="ng-q-when-page"><p>Wraps an object that might be a value or a (3rd party) then-able promise into a $q promise.
|
||
This is useful when you are dealing with an object that might or might not be a promise, or if
|
||
the promise comes from a source that can't be trusted.</p>
|
||
</div><h5 id="parameters">Parameters</h5><table class="variables-matrix table table-bordered table-striped"><thead><tr><th>Param</th><th>Type</th><th>Details</th></tr></thead><tbody><tr><td>value</td><td><a href="" class="label type-hint type-hint-object">*</a></td><td><div class="ng-q-when-page"><p>Value or a promise</p>
|
||
</div></td></tr></tbody></table><h5 id="returns">Returns</h5><table class="variables-matrix"><tr><td><a href="" class="label type-hint type-hint-promise">Promise</a></td><td><div class="ng-q-when-page"><p>Returns a promise of the passed value or promise</p>
|
||
</div></td></tr></table></div>
|
||
</li>
|
||
</ul>
|
||
</div>
|
||
</div>
|