Subversion Repositories Applications.papyrus

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
2150 mathias 1
if(!dojo._hasResource["dojo._base.Deferred"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
2
dojo._hasResource["dojo._base.Deferred"] = true;
3
dojo.provide("dojo._base.Deferred");
4
dojo.require("dojo._base.lang");
5
 
6
dojo.Deferred = function(/*Function?*/ canceller){
7
	// summary:
8
	//		Encapsulates a sequence of callbacks in response to a value that
9
	//		may not yet be available.  This is modeled after the Deferred class
10
	//		from Twisted <http://twistedmatrix.com>.
11
	// description:
12
	//		JavaScript has no threads, and even if it did, threads are hard.
13
	//		Deferreds are a way of abstracting non-blocking events, such as the
14
	//		final response to an XMLHttpRequest. Deferreds create a promise to
15
	//		return a response a some point in the future and an easy way to
16
	//		register your interest in receiving that response.
17
	//
18
	//		The most important methods for Deffered users are:
19
	//
20
	//			* addCallback(handler)
21
	//			* addErrback(handler)
22
	//			* callback(result)
23
	//			* errback(result)
24
	//
25
	//		In general, when a function returns a Deferred, users then "fill
26
	//		in" the second half of the contract by registering callbacks and
27
	//		error handlers. You may register as many callback and errback
28
	//		handlers as you like and they will be executed in the order
29
	//		registered when a result is provided. Usually this result is
30
	//		provided as the result of an asynchronous operation. The code
31
	//		"managing" the Deferred (the code that made the promise to provide
32
	//		an answer later) will use the callback() and errback() methods to
33
	//		communicate with registered listeners about the result of the
34
	//		operation. At this time, all registered result handlers are called
35
	//		*with the most recent result value*.
36
	//
37
	//		Deferred callback handlers are treated as a chain, and each item in
38
	//		the chain is required to return a value that will be fed into
39
	//		successive handlers. The most minimal callback may be registered
40
	//		like this:
41
	//
42
	//		|	var d = new dojo.Deferred();
43
	//		|	d.addCallback(function(result){ return result; });
44
	//
45
	//		Perhaps the most common mistake when first using Deferreds is to
46
	//		forget to return a value (in most cases, the value you were
47
	//		passed).
48
	//
49
	//		The sequence of callbacks is internally represented as a list of
50
	//		2-tuples containing the callback/errback pair.  For example, the
51
	//		following call sequence:
52
	//
53
	//		|	var d = new dojo.Deferred();
54
	//		|	d.addCallback(myCallback);
55
	//		|	d.addErrback(myErrback);
56
	//		|	d.addBoth(myBoth);
57
	//		|	d.addCallbacks(myCallback, myErrback);
58
	//
59
	//		is translated into a Deferred with the following internal
60
	//		representation:
61
	//
62
	//		|	[
63
	//		|		[myCallback, null],
64
	//		|		[null, myErrback],
65
	//		|		[myBoth, myBoth],
66
	//		|		[myCallback, myErrback]
67
	//		|	]
68
	//
69
	//		The Deferred also keeps track of its current status (fired).  Its
70
	//		status may be one of three things:
71
	//
72
	//			* -1: no value yet (initial condition)
73
	//			* 0: success
74
	//			* 1: error
75
	//
76
	//		A Deferred will be in the error state if one of the following three
77
	//		conditions are met:
78
	//
79
	//			1. The result given to callback or errback is "instanceof" Error
80
	//			2. The previous callback or errback raised an exception while
81
	//			   executing
82
	//			3. The previous callback or errback returned a value
83
	//			   "instanceof" Error
84
	//
85
	//		Otherwise, the Deferred will be in the success state. The state of
86
	//		the Deferred determines the next element in the callback sequence
87
	//		to run.
88
	//
89
	//		When a callback or errback occurs with the example deferred chain,
90
	//		something equivalent to the following will happen (imagine
91
	//		that exceptions are caught and returned):
92
	//
93
	//		|	// d.callback(result) or d.errback(result)
94
	//		|	if(!(result instanceof Error)){
95
	//		|		result = myCallback(result);
96
	//		|	}
97
	//		|	if(result instanceof Error){
98
	//		|		result = myErrback(result);
99
	//		|	}
100
	//		|	result = myBoth(result);
101
	//		|	if(result instanceof Error){
102
	//		|		result = myErrback(result);
103
	//		|	}else{
104
	//		|		result = myCallback(result);
105
	//		|	}
106
	//
107
	//		The result is then stored away in case another step is added to the
108
	//		callback sequence.	Since the Deferred already has a value
109
	//		available, any new callbacks added will be called immediately.
110
	//
111
	//		There are two other "advanced" details about this implementation
112
	//		that are useful:
113
	//
114
	//		Callbacks are allowed to return Deferred instances themselves, so
115
	//		you can build complicated sequences of events with ease.
116
	//
117
	//		The creator of the Deferred may specify a canceller.  The canceller
118
	//		is a function that will be called if Deferred.cancel is called
119
	//		before the Deferred fires. You can use this to implement clean
120
	//		aborting of an XMLHttpRequest, etc. Note that cancel will fire the
121
	//		deferred with a CancelledError (unless your canceller returns
122
	//		another kind of error), so the errbacks should be prepared to
123
	//		handle that error for cancellable Deferreds.
124
	// example:
125
	//	|	var deferred = new dojo.Deferred();
126
	//	|	setTimeout(function(){ deferred.callback({success: true}); }, 1000);
127
	//	|	return deferred;
128
	// example:
129
	//		Deferred objects are often used when making code asynchronous. It
130
	//		may be easiest to write functions in a synchronous manner and then
131
	//		split code using a deferred to trigger a response to a long-lived
132
	//		operation. For example, instead of register a callback function to
133
	//		denote when a rendering operation completes, the function can
134
	//		simply return a deferred:
135
	//
136
	//		|	// callback style:
137
	//		|	function renderLotsOfData(data, callback){
138
	//		|		var success = false
139
	//		|		try{
140
	//		|			for(var x in data){
141
	//		|				renderDataitem(data[x]);
142
	//		|			}
143
	//		|			success = true;
144
	//		|		}catch(e){ }
145
	//		|		if(callback){
146
	//		|			callback(success);
147
	//		|		}
148
	//		|	}
149
	//
150
	//		|	// using callback style
151
	//		|	renderLotsOfData(someDataObj, function(success){
152
	//		|		// handles success or failure
153
	//		|		if(!success){
154
	//		|			promptUserToRecover();
155
	//		|		}
156
	//		|	});
157
	//		|	// NOTE: no way to add another callback here!!
158
	// example:
159
	//		Using a Deferred doesn't simplify the sending code any, but it
160
	//		provides a standard interface for callers and senders alike,
161
	//		providing both with a simple way to service multiple callbacks for
162
	//		an operation and freeing both sides from worrying about details
163
	//		such as "did this get called already?". With Deferreds, new
164
	//		callbacks can be added at any time.
165
	//
166
	//		|	// Deferred style:
167
	//		|	function renderLotsOfData(data){
168
	//		|		var d = new dojo.Deferred();
169
	//		|		try{
170
	//		|			for(var x in data){
171
	//		|				renderDataitem(data[x]);
172
	//		|			}
173
	//		|			d.callback(true);
174
	//		|		}catch(e){
175
	//		|			d.errback(new Error("rendering failed"));
176
	//		|		}
177
	//		|		return d;
178
	//		|	}
179
	//
180
	//		|	// using Deferred style
181
	//		|	renderLotsOfData(someDataObj).addErrback(function(){
182
	//		|		promptUserToRecover();
183
	//		|	});
184
	//		|	// NOTE: addErrback and addCallback both return the Deferred
185
	//		|	// again, so we could chain adding callbacks or save the
186
	//		|	// deferred for later should we need to be notified again.
187
	// example:
188
	//		In this example, renderLotsOfData is syncrhonous and so both
189
	//		versions are pretty artificial. Putting the data display on a
190
	//		timeout helps show why Deferreds rock:
191
	//
192
	//		|	// Deferred style and async func
193
	//		|	function renderLotsOfData(data){
194
	//		|		var d = new dojo.Deferred();
195
	//		|		setTimeout(function(){
196
	//		|			try{
197
	//		|				for(var x in data){
198
	//		|					renderDataitem(data[x]);
199
	//		|				}
200
	//		|				d.callback(true);
201
	//		|			}catch(e){
202
	//		|				d.errback(new Error("rendering failed"));
203
	//		|			}
204
	//		|		}, 100);
205
	//		|		return d;
206
	//		|	}
207
	//
208
	//		|	// using Deferred style
209
	//		|	renderLotsOfData(someDataObj).addErrback(function(){
210
	//		|		promptUserToRecover();
211
	//		|	});
212
	//
213
	//		Note that the caller doesn't have to change his code at all to
214
	//		handle the asynchronous case.
215
 
216
	this.chain = [];
217
	this.id = this._nextId();
218
	this.fired = -1;
219
	this.paused = 0;
220
	this.results = [null, null];
221
	this.canceller = canceller;
222
	this.silentlyCancelled = false;
223
};
224
 
225
dojo.extend(dojo.Deferred, {
226
	/*
227
	makeCalled: function(){
228
		// summary:
229
		//		returns a new, empty deferred, which is already in the called
230
		//		state. Calling callback() or errback() on this deferred will
231
		//		yeild an error and adding new handlers to it will result in
232
		//		them being called immediately.
233
		var deferred = new dojo.Deferred();
234
		deferred.callback();
235
		return deferred;
236
	},
237
 
238
	toString: function(){
239
		var state;
240
		if(this.fired == -1){
241
			state = 'unfired';
242
		}else{
243
			state = this.fired ? 'success' : 'error';
244
		}
245
		return 'Deferred(' + this.id + ', ' + state + ')';
246
	},
247
	*/
248
 
249
	_nextId: (function(){
250
		var n = 1;
251
		return function(){ return n++; };
252
	})(),
253
 
254
	cancel: function(){
255
		// summary:
256
		//		Cancels a Deferred that has not yet received a value, or is
257
		//		waiting on another Deferred as its value.
258
		// description:
259
		//		If a canceller is defined, the canceller is called. If the
260
		//		canceller did not return an error, or there was no canceller,
261
		//		then the errback chain is started.
262
		var err;
263
		if(this.fired == -1){
264
			if(this.canceller){
265
				err = this.canceller(this);
266
			}else{
267
				this.silentlyCancelled = true;
268
			}
269
			if(this.fired == -1){
270
				if(!(err instanceof Error)){
271
					var res = err;
272
					err = new Error("Deferred Cancelled");
273
					err.dojoType = "cancel";
274
					err.cancelResult = res;
275
				}
276
				this.errback(err);
277
			}
278
		}else if(	(this.fired == 0) &&
279
					(this.results[0] instanceof dojo.Deferred)
280
		){
281
			this.results[0].cancel();
282
		}
283
	},
284
 
285
 
286
	_resback: function(res){
287
		// summary:
288
		//		The private primitive that means either callback or errback
289
		this.fired = ((res instanceof Error) ? 1 : 0);
290
		this.results[this.fired] = res;
291
		this._fire();
292
	},
293
 
294
	_check: function(){
295
		if(this.fired != -1){
296
			if(!this.silentlyCancelled){
297
				throw new Error("already called!");
298
			}
299
			this.silentlyCancelled = false;
300
			return;
301
		}
302
	},
303
 
304
	callback: function(res){
305
		// summary:	Begin the callback sequence with a non-error value.
306
 
307
		/*
308
		callback or errback should only be called once on a given
309
		Deferred.
310
		*/
311
		this._check();
312
		this._resback(res);
313
	},
314
 
315
	errback: function(/*Error*/res){
316
		// summary:
317
		//		Begin the callback sequence with an error result.
318
		this._check();
319
		if(!(res instanceof Error)){
320
			res = new Error(res);
321
		}
322
		this._resback(res);
323
	},
324
 
325
	addBoth: function(/*Function||Object*/cb, /*Optional, String*/cbfn){
326
		// summary:
327
		//		Add the same function as both a callback and an errback as the
328
		//		next element on the callback sequence.	This is useful for code
329
		//		that you want to guarantee to run, e.g. a finalizer.
330
		var enclosed = dojo.hitch(cb, cbfn);
331
		if(arguments.length > 2){
332
			enclosed = dojo.partial(enclosed, arguments, 2);
333
		}
334
		return this.addCallbacks(enclosed, enclosed);
335
	},
336
 
337
	addCallback: function(cb, cbfn){
338
		// summary:
339
		//		Add a single callback to the end of the callback sequence.
340
		var enclosed = dojo.hitch(cb, cbfn);
341
		if(arguments.length > 2){
342
			enclosed = dojo.partial(enclosed, arguments, 2);
343
		}
344
		return this.addCallbacks(enclosed, null);
345
	},
346
 
347
	addErrback: function(cb, cbfn){
348
		// summary:
349
		//		Add a single callback to the end of the callback sequence.
350
		var enclosed = dojo.hitch(cb, cbfn);
351
		if(arguments.length > 2){
352
			enclosed = dojo.partial(enclosed, arguments, 2);
353
		}
354
		return this.addCallbacks(null, enclosed);
355
	},
356
 
357
	addCallbacks: function(cb, eb){
358
		// summary:
359
		//		Add separate callback and errback to the end of the callback
360
		//		sequence.
361
		this.chain.push([cb, eb])
362
		if(this.fired >= 0){
363
			this._fire();
364
		}
365
		return this;
366
	},
367
 
368
	_fire: function(){
369
		// summary:
370
		//		Used internally to exhaust the callback sequence when a result
371
		//		is available.
372
		var chain = this.chain;
373
		var fired = this.fired;
374
		var res = this.results[fired];
375
		var self = this;
376
		var cb = null;
377
		while(
378
			(chain.length > 0) &&
379
			(this.paused == 0)
380
		){
381
			// Array
382
			var f = chain.shift()[fired];
383
			if(!f){ continue; }
384
			try{
385
				res = f(res);
386
				fired = ((res instanceof Error) ? 1 : 0);
387
				if(res instanceof dojo.Deferred){
388
					cb = function(res){
389
						self._resback(res);
390
						// inlined from _pause()
391
						self.paused--;
392
						if(
393
							(self.paused == 0) &&
394
							(self.fired >= 0)
395
						){
396
							self._fire();
397
						}
398
					}
399
					// inlined from _unpause
400
					this.paused++;
401
				}
402
			}catch(err){
403
				console.debug(err);
404
				fired = 1;
405
				res = err;
406
			}
407
		}
408
		this.fired = fired;
409
		this.results[fired] = res;
410
		if((cb)&&(this.paused)){
411
			// this is for "tail recursion" in case the dependent
412
			// deferred is already fired
413
			res.addBoth(cb);
414
		}
415
	}
416
});
417
 
418
}