| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 2013, 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 part of dart.async; | |
| 6 | |
| 7 typedef void _AsyncCallback(); | |
| 8 | |
| 9 class _AsyncCallbackEntry { | |
| 10 final _AsyncCallback callback; | |
| 11 _AsyncCallbackEntry next; | |
| 12 _AsyncCallbackEntry(this.callback); | |
| 13 } | |
| 14 | |
| 15 /** Head of single linked list of pending callbacks. */ | |
| 16 _AsyncCallbackEntry _nextCallback; | |
| 17 /** Tail of single linked list of pending callbacks. */ | |
| 18 _AsyncCallbackEntry _lastCallback; | |
| 19 /** | |
| 20 * Tail of priority callbacks added by the currently executing callback. | |
| 21 * | |
| 22 * Priority callbacks are put at the beginning of the | |
| 23 * callback queue, so that if one callback schedules more than one | |
| 24 * priority callback, they are still enqueued in scheduling order. | |
| 25 */ | |
| 26 _AsyncCallbackEntry _lastPriorityCallback; | |
| 27 /** | |
| 28 * Whether we are currently inside the callback loop. | |
| 29 * | |
| 30 * If we are inside the loop, we never need to schedule the loop, | |
| 31 * even if adding a first element. | |
| 32 */ | |
| 33 bool _isInCallbackLoop = false; | |
| 34 | |
| 35 void _asyncRunCallbackLoop() { | |
| 36 while (_nextCallback != null) { | |
| 37 _lastPriorityCallback = null; | |
| 38 _AsyncCallbackEntry entry = _nextCallback; | |
| 39 _nextCallback = entry.next; | |
| 40 if (_nextCallback == null) _lastCallback = null; | |
| 41 entry.callback(); | |
| 42 } | |
| 43 } | |
| 44 | |
| 45 void _asyncRunCallback() { | |
| 46 _isInCallbackLoop = true; | |
| 47 try { | |
| 48 _asyncRunCallbackLoop(); | |
| 49 } finally { | |
| 50 _lastPriorityCallback = null; | |
| 51 _isInCallbackLoop = false; | |
| 52 if (_nextCallback != null) _AsyncRun._scheduleImmediate(_asyncRunCallback); | |
| 53 } | |
| 54 } | |
| 55 | |
| 56 /** | |
| 57 * Schedules a callback to be called as a microtask. | |
| 58 * | |
| 59 * The microtask is called after all other currently scheduled | |
| 60 * microtasks, but as part of the current system event. | |
| 61 */ | |
| 62 void _scheduleAsyncCallback(callback) { | |
| 63 // Optimizing a group of Timer.run callbacks to be executed in the | |
| 64 // same Timer callback. | |
| 65 if (_nextCallback == null) { | |
| 66 _nextCallback = _lastCallback = new _AsyncCallbackEntry(callback); | |
| 67 if (!_isInCallbackLoop) { | |
| 68 _AsyncRun._scheduleImmediate(_asyncRunCallback); | |
| 69 } | |
| 70 } else { | |
| 71 _AsyncCallbackEntry newEntry = new _AsyncCallbackEntry(callback); | |
| 72 _lastCallback.next = newEntry; | |
| 73 _lastCallback = newEntry; | |
| 74 } | |
| 75 } | |
| 76 | |
| 77 /** | |
| 78 * Schedules a callback to be called before all other currently scheduled ones. | |
| 79 * | |
| 80 * This callback takes priority over existing scheduled callbacks. | |
| 81 * It is only used internally to give higher priority to error reporting. | |
| 82 */ | |
| 83 void _schedulePriorityAsyncCallback(callback) { | |
| 84 _AsyncCallbackEntry entry = new _AsyncCallbackEntry(callback); | |
| 85 if (_nextCallback == null) { | |
| 86 _scheduleAsyncCallback(callback); | |
| 87 _lastPriorityCallback = _lastCallback; | |
| 88 } else if (_lastPriorityCallback == null) { | |
| 89 entry.next = _nextCallback; | |
| 90 _nextCallback = _lastPriorityCallback = entry; | |
| 91 } else { | |
| 92 entry.next = _lastPriorityCallback.next; | |
| 93 _lastPriorityCallback.next = entry; | |
| 94 _lastPriorityCallback = entry; | |
| 95 if (entry.next == null) { | |
| 96 _lastCallback = entry; | |
| 97 } | |
| 98 } | |
| 99 } | |
| 100 | |
| 101 /** | |
| 102 * Runs a function asynchronously. | |
| 103 * | |
| 104 * Callbacks registered through this function are always executed in order and | |
| 105 * are guaranteed to run before other asynchronous events (like [Timer] events, | |
| 106 * or DOM events). | |
| 107 * | |
| 108 * **Warning:** it is possible to starve the DOM by registering asynchronous | |
| 109 * callbacks through this method. For example the following program runs | |
| 110 * the callbacks without ever giving the Timer callback a chance to execute: | |
| 111 * | |
| 112 * main() { | |
| 113 * Timer.run(() { print("executed"); }); // Will never be executed. | |
| 114 * foo() { | |
| 115 * scheduleMicrotask(foo); // Schedules [foo] in front of other events. | |
| 116 * } | |
| 117 * foo(); | |
| 118 * } | |
| 119 * | |
| 120 * ## Other resources | |
| 121 * | |
| 122 * * [The Event Loop and Dart](https://www.dartlang.org/articles/event-loop/): | |
| 123 * Learn how Dart handles the event queue and microtask queue, so you can write | |
| 124 * better asynchronous code with fewer surprises. | |
| 125 */ | |
| 126 void scheduleMicrotask(void callback()) { | |
| 127 if (identical(_ROOT_ZONE, Zone.current)) { | |
| 128 // No need to bind the callback. We know that the root's scheduleMicrotask | |
| 129 // will be invoked in the root zone. | |
| 130 _rootScheduleMicrotask(null, null, _ROOT_ZONE, callback); | |
| 131 return; | |
| 132 } | |
| 133 Zone.current.scheduleMicrotask( | |
| 134 Zone.current.bindCallback(callback, runGuarded: true)); | |
| 135 } | |
| 136 | |
| 137 class _AsyncRun { | |
| 138 /** Schedule the given callback before any other event in the event-loop. */ | |
| 139 static void _scheduleImmediate(void callback()) { | |
| 140 scheduleImmediateClosure(callback); | |
| 141 } | |
| 142 | |
| 143 static final Function scheduleImmediateClosure = | |
| 144 _initializeScheduleImmediate(); | |
| 145 | |
| 146 static Function _initializeScheduleImmediate() { | |
| 147 requiresPreamble(); | |
| 148 if (JS('', 'self.scheduleImmediate') != null) { | |
| 149 return _scheduleImmediateJsOverride; | |
| 150 } | |
| 151 if (JS('', 'self.MutationObserver') != null && | |
| 152 JS('', 'self.document') != null) { | |
| 153 // Use mutationObservers. | |
| 154 var div = JS('', 'self.document.createElement("div")'); | |
| 155 var span = JS('', 'self.document.createElement("span")'); | |
| 156 var storedCallback; | |
| 157 | |
| 158 internalCallback(_) { | |
| 159 leaveJsAsync(); | |
| 160 var f = storedCallback; | |
| 161 storedCallback = null; | |
| 162 f(); | |
| 163 }; | |
| 164 | |
| 165 var observer = JS('', 'new self.MutationObserver(#)', | |
| 166 convertDartClosureToJS(internalCallback, 1)); | |
| 167 JS('', '#.observe(#, { childList: true })', | |
| 168 observer, div); | |
| 169 | |
| 170 return (void callback()) { | |
| 171 assert(storedCallback == null); | |
| 172 enterJsAsync(); | |
| 173 storedCallback = callback; | |
| 174 // Because of a broken shadow-dom polyfill we have to change the | |
| 175 // children instead a cheap property. | |
| 176 // See https://github.com/Polymer/ShadowDOM/issues/468 | |
| 177 JS('', '#.firstChild ? #.removeChild(#): #.appendChild(#)', | |
| 178 div, div, span, div, span); | |
| 179 }; | |
| 180 } else if (JS('', 'self.setImmediate') != null) { | |
| 181 return _scheduleImmediateWithSetImmediate; | |
| 182 } | |
| 183 // TODO(20055): We should use DOM promises when available. | |
| 184 return _scheduleImmediateWithTimer; | |
| 185 } | |
| 186 | |
| 187 static void _scheduleImmediateJsOverride(void callback()) { | |
| 188 internalCallback() { | |
| 189 leaveJsAsync(); | |
| 190 callback(); | |
| 191 }; | |
| 192 enterJsAsync(); | |
| 193 JS('void', 'self.scheduleImmediate(#)', | |
| 194 convertDartClosureToJS(internalCallback, 0)); | |
| 195 } | |
| 196 | |
| 197 static void _scheduleImmediateWithSetImmediate(void callback()) { | |
| 198 internalCallback() { | |
| 199 leaveJsAsync(); | |
| 200 callback(); | |
| 201 }; | |
| 202 enterJsAsync(); | |
| 203 JS('void', 'self.setImmediate(#)', | |
| 204 convertDartClosureToJS(internalCallback, 0)); | |
| 205 } | |
| 206 | |
| 207 static void _scheduleImmediateWithTimer(void callback()) { | |
| 208 Timer._createTimer(Duration.ZERO, callback); | |
| 209 } | |
| 210 } | |
| OLD | NEW |