forked from jquery/api.jquery.com
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathjQuery.Callbacks.xml
270 lines (242 loc) · 9.69 KB
/
jQuery.Callbacks.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
<?xml version="1.0"?>
<entry name="jQuery.Callbacks" type="method" return="Callbacks">
<title>jQuery.Callbacks()</title>
<signature>
<added>1.7</added>
<argument name="flags" type="String">
<desc>An optional list of space-separated flags that change how the callback list behaves.</desc>
</argument>
</signature>
<desc>A multi-purpose callbacks list object that provides a powerful way to manage callback lists.</desc>
<longdesc>
<p>The <code>$.Callbacks()</code> function is internally used to provide the base functionality behind the jQuery <code>$.ajax()</code> and <code>$.Deferred()</code> components. It can be used as a similar base to define functionality for new components.</p>
<p><code>$.Callbacks()</code> supports a number of methods including <code><a href="/callbacks.add/">callbacks.add()</a></code>,<code><a href="/callbacks.remove/">callbacks.remove()</a></code>, <code><a href="/callbacks.fire/">callbacks.fire()</a></code> and <code><a href="/callbacks.disable/">callbacks.disable()</a></code>.</p>
<h3 id="getting-started">Getting started</h3>
<p>The following are two sample methods named <code>fn1</code> and <code>fn2</code>:</p>
<pre><code>
function fn1( value ) {
console.log( value );
}
function fn2( value ) {
console.log( "fn2 says: " + value );
return false;
}
</code></pre>
<p>These can be added as callbacks to a <code>$.Callbacks</code> list and invoked as follows:</p>
<pre><code>
var callbacks = $.Callbacks();
callbacks.add( fn1 );
// Outputs: foo!
callbacks.fire( "foo!" );
callbacks.add( fn2 );
// Outputs: bar!, fn2 says: bar!
callbacks.fire( "bar!" );
</code></pre>
<p>The result of this is that it becomes simple to construct complex lists of callbacks where input values can be passed through to as many functions as needed with ease.</p>
<p>Two specific methods were being used above: <code>.add()</code> and <code>.fire()</code>. The <code>.add()</code> method supports adding new callbacks to the callback list, while the <code>.fire()</code> method executes the added functions and provides a way to pass arguments to be processed by the callbacks in the same list.</p>
<p>Another method supported by <code>$.Callbacks</code> is <code>.remove()</code>, which has the ability to remove a particular callback from the callback list. Here's a practical example of <code>.remove()</code> being used:</p>
<pre><code>
var callbacks = $.Callbacks();
callbacks.add( fn1 );
// Outputs: foo!
callbacks.fire( "foo!" );
callbacks.add( fn2 );
// Outputs: bar!, fn2 says: bar!
callbacks.fire( "bar!" );
callbacks.remove( fn2 );
// Only outputs foobar, as fn2 has been removed.
callbacks.fire( "foobar" );
</code></pre>
<h3 id="supported-flags">Supported Flags</h3>
<p>The <code>flags</code> argument is an optional argument to <code>$.Callbacks()</code>, structured as a list of space-separated strings that change how the callback list behaves (eg. <code>$.Callbacks( "unique stopOnFalse" )</code>).</p>
<h2>Possible flags:</h2>
<ul>
<li><code>once</code>: Ensures the callback list can only be fired once (like a Deferred).</li>
<li><code>memory</code>: Keeps track of previous values and will call any callback added after the list has been fired right away with the latest "memorized" values (like a Deferred).</li>
<li><code>unique</code>: Ensures a callback can only be added once (so there are no duplicates in the list).</li>
<li><code>stopOnFalse</code>: Interrupts callings when a callback returns false.</li>
</ul>
<p>By default a callback list will act like an event callback list and can be "fired" multiple times.</p>
<p>For examples of how <code>flags</code> should ideally be used, see below:</p>
<h2 id="once"><code>$.Callbacks( "once" )</code>:</h2>
<pre><code>
var callbacks = $.Callbacks( "once" );
callbacks.add( fn1 );
callbacks.fire( "foo" );
callbacks.add( fn2 );
callbacks.fire( "bar" );
callbacks.remove( fn2 );
callbacks.fire( "foobar" );
/*
output:
foo
*/
</code></pre>
<h2 id="memory"><code>$.Callbacks( "memory" )</code>:</h2>
<pre><code>
var callbacks = $.Callbacks( "memory" );
callbacks.add( fn1 );
callbacks.fire( "foo" );
callbacks.add( fn2 );
callbacks.fire( "bar" );
callbacks.remove( fn2 );
callbacks.fire( "foobar" );
/*
output:
foo
fn2 says:foo
bar
fn2 says:bar
foobar
*/
</code></pre>
<h2 id="unique"><code>$.Callbacks( "unique" )</code>:</h2>
<pre><code>
var callbacks = $.Callbacks( "unique" );
callbacks.add( fn1 );
callbacks.fire( "foo" );
callbacks.add( fn1 ); // Repeat addition
callbacks.add( fn2 );
callbacks.fire( "bar" );
callbacks.remove( fn2 );
callbacks.fire( "foobar" );
/*
output:
foo
bar
fn2 says:bar
foobar
*/
</code></pre>
<h2 id="stopOnFalse"><code>$.Callbacks( "stopOnFalse" )</code>:</h2>
<pre><code>
function fn1( value ) {
console.log( value );
return false;
}
function fn2( value ) {
fn1( "fn2 says: " + value );
return false;
}
var callbacks = $.Callbacks( "stopOnFalse" );
callbacks.add( fn1 );
callbacks.fire( "foo" );
callbacks.add( fn2 );
callbacks.fire( "bar" );
callbacks.remove( fn2 );
callbacks.fire( "foobar" );
/*
output:
foo
bar
foobar
*/
</code></pre>
<p>Because <code>$.Callbacks()</code> supports a list of flags rather than just one, setting several flags has a cumulative effect similar to "&&". This means it's possible to combine flags to create callback lists that, say, both are <i>unique</i> and <i>ensure if list was already fired, adding more callbacks will have it called with the latest fired value</i> (i.e. <code>$.Callbacks("unique memory")</code>).</p>
<h2 id="unique-memory"><code>$.Callbacks( 'unique memory' )</code>:</h2>
<pre><code>
function fn1( value ) {
console.log( value );
return false;
}
function fn2( value ) {
fn1( "fn2 says: " + value );
return false;
}
var callbacks = $.Callbacks( "unique memory" );
callbacks.add( fn1 );
callbacks.fire( "foo" );
callbacks.add( fn1 ); // Repeat addition
callbacks.add( fn2 );
callbacks.fire( "bar" );
callbacks.add( fn2 );
callbacks.fire( "baz" );
callbacks.remove( fn2 );
callbacks.fire( "foobar" );
/*
output:
foo
fn2 says:foo
bar
fn2 says:bar
baz
fn2 says:baz
foobar
*/
</code></pre>
<p>Flag combinations with <code>$.Callbacks()</code> are internally in jQuery for the <code>.done()</code> and <code>.fail()</code> functions on a Deferred — both of which use <code>$.Callbacks('memory once')</code>.</p>
<p>The methods of <code>$.Callbacks</code> can also be detached, should there be a need to define short-hand versions for convenience:</p>
<pre><code>
var callbacks = $.Callbacks(),
add = callbacks.add,
remove = callbacks.remove,
fire = callbacks.fire;
add( fn1 );
fire( "hello world" );
remove( fn1 );
</code></pre>
<h3 id="pubsub">$.Callbacks, $.Deferred and Pub/Sub</h3>
<p>The general idea behind pub/sub (Publish/Subscribe, or, the Observer pattern) is the promotion of loose coupling in applications. Rather than single objects calling on the methods of other objects, an object instead subscribes to a specific task or activity of another object and is notified when it occurs. Observers are also called Subscribers, and we refer to the object being observed as the Publisher (or the subject). Publishers notify subscribers when events occur.</p>
<p>To demonstrate the component-creation capabilities of <code>$.Callbacks()</code>, it's possible to implement a Pub/Sub system using only callback lists. Using <code>$.Callbacks</code> as a topics queue, a system for publishing and subscribing to topics can be implemented as follows:</p>
<pre><code>
var topics = {};
jQuery.Topic = function( id ) {
var callbacks, method,
topic = id && topics[ id ];
if ( !topic ) {
callbacks = jQuery.Callbacks();
topic = {
publish: callbacks.fire,
subscribe: callbacks.add,
unsubscribe: callbacks.remove
};
if ( id ) {
topics[ id ] = topic;
}
}
return topic;
};
</code></pre>
<p>This can then be used by parts of your application to publish and subscribe to events of interest quite easily:</p>
<pre><code>
// Subscribers
$.Topic( "mailArrived" ).subscribe( fn1 );
$.Topic( "mailArrived" ).subscribe( fn2 );
$.Topic( "mailSent" ).subscribe( fn1 );
// Publisher
$.Topic( "mailArrived" ).publish( "hello world!" );
$.Topic( "mailSent" ).publish( "woo! mail!" );
// Here, "hello world!" gets pushed to fn1 and fn2
// when the "mailArrived" notification is published
// with "woo! mail!" also being pushed to fn1 when
// the "mailSent" notification is published.
/*
output:
hello world!
fn2 says: hello world!
woo! mail!
*/
</code></pre>
<p>While this is useful, the implementation can be taken further. Using <code>$.Deferreds</code>, it's possible to ensure publishers only publish notifications for subscribers once particular tasks have been completed (resolved). See the below code sample for some further comments on how this could be used in practice:</p>
<pre><code>
// Subscribe to the mailArrived notification
$.Topic( "mailArrived" ).subscribe( fn1 );
// Create a new instance of Deferreds
var dfd = $.Deferred();
// Define a new topic (without directly publishing)
var topic = $.Topic( "mailArrived" );
// When the deferred has been resolved, publish a
// notification to subscribers
dfd.done( topic.publish );
// Here the Deferred is being resolved with a message
// that will be passed back to subscribers. It's possible to
// easily integrate this into a more complex routine
// (eg. waiting on an Ajax call to complete) so that
// messages are only published once the task has actually
// finished.
dfd.resolve( "it's been published!" );
</code></pre>
</longdesc>
<category slug="callbacks-object"/>
<category slug="version/1.7"/>
</entry>