| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file | |
| 2 // for details. All rights reserved. Use of this source code is governed by a | |
| 3 // BSD-style license that can be found in the LICENSE file. | |
| 4 | |
| 5 /** | |
| 6 * Accumulates change events from several observable objects. | |
| 7 * | |
| 8 * wrap() is public and used by client code. The other methods are used by | |
| 9 * AbstractObservable, which works with this class to implement batching. | |
| 10 */ | |
| 11 class EventBatch { | |
| 12 | |
| 13 /** The current active batch, if any. */ | |
| 14 static EventBatch current; | |
| 15 | |
| 16 /** Used to generate unique ids for observable objects. */ | |
| 17 static int nextUid; | |
| 18 | |
| 19 /** Map from observable object's uid to their tracked events. */ | |
| 20 // TODO(sigmund): use [Observable] instead of [int] when [Map] can support it, | |
| 21 Map<int, EventSummary> summaries; | |
| 22 | |
| 23 /** Whether this batch is currently firing and therefore is sealed. */ | |
| 24 bool sealed = false; | |
| 25 | |
| 26 /** | |
| 27 * Private constructor that shouldn't be used externally. Use [wrap] to ensure | |
| 28 * that a batch exists when running a function. | |
| 29 */ | |
| 30 EventBatch._internal() : summaries = new Map<int, EventSummary>(); | |
| 31 | |
| 32 /** | |
| 33 * Ensure there is an event batch where [userFunction] can accumuluate events. | |
| 34 * When the batch is complete, fire all events at once. | |
| 35 */ | |
| 36 static Function wrap(userFunction(var a)) { | |
| 37 return (e) { | |
| 38 if (current == null) { | |
| 39 // Not in a batch so create one. | |
| 40 final batch = new EventBatch._internal(); | |
| 41 current = batch; | |
| 42 var result = null; | |
| 43 try { | |
| 44 // TODO(jmesserly): don't return here, otherwise an exception in | |
| 45 // the finally clause will cause it to rerun. See bug#5350131. | |
| 46 result = userFunction(e); | |
| 47 } finally { | |
| 48 assert(current == batch); // no one should've changed this | |
| 49 // TODO(jmesserly): VM doesn't seem to like nested try/finally, so | |
| 50 // set current to null before _notify. That will ensure we're back | |
| 51 // to the right state, even if _notify throws. | |
| 52 current = null; | |
| 53 batch._notify(); | |
| 54 } | |
| 55 return result; | |
| 56 } else { | |
| 57 // Already in a batch, so just use it. | |
| 58 // TODO(rnystrom): Re-entrant calls to wrap() are kind of hairy. They | |
| 59 // can occur in at least one known place: | |
| 60 // 1. You respond to an event handler by calling a function with wrap() | |
| 61 // (i.e. the normal way we wrap event handlers). | |
| 62 // 2. In that handler, you spawn an XHR. You give it a callback which | |
| 63 // is also calling wrap, so that when it's later invoked, that is in | |
| 64 // a batch too. | |
| 65 // 3. Because of an error the XHR fails and calls the callback | |
| 66 // immediately instead of unwinding the stack past the first wrap() | |
| 67 // and then calling it asynchronously. | |
| 68 // This check handles that, but ideally we'd have a more elegant way of | |
| 69 // notifying after a series of changes like a onEventHandlerFinished | |
| 70 // event or something built into the DOM API. | |
| 71 return userFunction(e); | |
| 72 } | |
| 73 }; | |
| 74 } | |
| 75 | |
| 76 /** Returns a unique global id for observable objects. */ | |
| 77 static int genUid() { | |
| 78 if (nextUid == null) { | |
| 79 nextUid = 1; | |
| 80 } | |
| 81 return nextUid++; | |
| 82 } | |
| 83 | |
| 84 /** Retrieves the events associated with {@code obj}. */ | |
| 85 EventSummary getEvents(Observable obj) { | |
| 86 int uid = obj.uid; | |
| 87 EventSummary summary = summaries[uid]; | |
| 88 if (summary == null) { | |
| 89 assert (!sealed); | |
| 90 summary = new EventSummary(obj); | |
| 91 summaries[uid] = summary; | |
| 92 } | |
| 93 return summary; | |
| 94 } | |
| 95 | |
| 96 /** Fires all events at once. */ | |
| 97 void _notify() { | |
| 98 assert(!sealed); | |
| 99 sealed = true; | |
| 100 for (final summary in summaries.getValues()) { | |
| 101 summary.notify(); | |
| 102 } | |
| 103 } | |
| 104 } | |
| OLD | NEW |