1 |
efrain |
1 |
YUI.add('timers', function (Y, NAME) {
|
|
|
2 |
|
|
|
3 |
/**
|
|
|
4 |
Provides utilities for timed asynchronous callback execution.
|
|
|
5 |
Y.soon is a setImmediate/process.nextTick/setTimeout wrapper.
|
|
|
6 |
|
|
|
7 |
This module includes [asap.js](https://github.com/kriskowal/asap) for scheduling
|
|
|
8 |
asynchronous tasks.
|
|
|
9 |
|
|
|
10 |
@module timers
|
|
|
11 |
@main timers
|
|
|
12 |
@author Steven Olmsted
|
|
|
13 |
**/
|
|
|
14 |
|
|
|
15 |
// Hack. asap.js is written as a Node module and expects require, module and
|
|
|
16 |
// global to be available in the module's scope.
|
|
|
17 |
var module = {},
|
|
|
18 |
global = Y.config.global;
|
|
|
19 |
|
|
|
20 |
// `asap` only requires a `queue` module that is bundled into this same file.
|
|
|
21 |
function require(mod) {
|
|
|
22 |
return Queue;
|
|
|
23 |
}
|
|
|
24 |
"use strict";
|
|
|
25 |
|
|
|
26 |
module.exports = Queue;
|
|
|
27 |
function Queue(capacity) {
|
|
|
28 |
this.capacity = this.snap(capacity);
|
|
|
29 |
this.length = 0;
|
|
|
30 |
this.front = 0;
|
|
|
31 |
this.initialize();
|
|
|
32 |
}
|
|
|
33 |
|
|
|
34 |
Queue.prototype.push = function (value) {
|
|
|
35 |
var length = this.length;
|
|
|
36 |
if (this.capacity <= length) {
|
|
|
37 |
this.grow(this.snap(this.capacity * this.growFactor));
|
|
|
38 |
}
|
|
|
39 |
var index = (this.front + length) & (this.capacity - 1);
|
|
|
40 |
this[index] = value;
|
|
|
41 |
this.length = length + 1;
|
|
|
42 |
};
|
|
|
43 |
|
|
|
44 |
Queue.prototype.shift = function () {
|
|
|
45 |
var front = this.front;
|
|
|
46 |
var result = this[front];
|
|
|
47 |
|
|
|
48 |
this[front] = void 0;
|
|
|
49 |
this.front = (front + 1) & (this.capacity - 1);
|
|
|
50 |
this.length--;
|
|
|
51 |
return result;
|
|
|
52 |
};
|
|
|
53 |
|
|
|
54 |
Queue.prototype.grow = function (capacity) {
|
|
|
55 |
var oldFront = this.front;
|
|
|
56 |
var oldCapacity = this.capacity;
|
|
|
57 |
var oldQueue = new Array(oldCapacity);
|
|
|
58 |
var length = this.length;
|
|
|
59 |
|
|
|
60 |
copy(this, 0, oldQueue, 0, oldCapacity);
|
|
|
61 |
this.capacity = capacity;
|
|
|
62 |
this.initialize();
|
|
|
63 |
this.front = 0;
|
|
|
64 |
if (oldFront + length <= oldCapacity) {
|
|
|
65 |
// Can perform direct linear copy
|
|
|
66 |
copy(oldQueue, oldFront, this, 0, length);
|
|
|
67 |
} else {
|
|
|
68 |
// Cannot perform copy directly, perform as much as possible at the
|
|
|
69 |
// end, and then copy the rest to the beginning of the buffer
|
|
|
70 |
var lengthBeforeWrapping =
|
|
|
71 |
length - ((oldFront + length) & (oldCapacity - 1));
|
|
|
72 |
copy(
|
|
|
73 |
oldQueue,
|
|
|
74 |
oldFront,
|
|
|
75 |
this,
|
|
|
76 |
0,
|
|
|
77 |
lengthBeforeWrapping
|
|
|
78 |
);
|
|
|
79 |
copy(
|
|
|
80 |
oldQueue,
|
|
|
81 |
0,
|
|
|
82 |
this,
|
|
|
83 |
lengthBeforeWrapping,
|
|
|
84 |
length - lengthBeforeWrapping
|
|
|
85 |
);
|
|
|
86 |
}
|
|
|
87 |
};
|
|
|
88 |
|
|
|
89 |
Queue.prototype.initialize = function () {
|
|
|
90 |
var length = this.capacity;
|
|
|
91 |
for (var i = 0; i < length; ++i) {
|
|
|
92 |
this[i] = void 0;
|
|
|
93 |
}
|
|
|
94 |
};
|
|
|
95 |
|
|
|
96 |
Queue.prototype.snap = function (capacity) {
|
|
|
97 |
if (typeof capacity !== "number") {
|
|
|
98 |
return this.minCapacity;
|
|
|
99 |
}
|
|
|
100 |
return pow2AtLeast(
|
|
|
101 |
Math.min(this.maxCapacity, Math.max(this.minCapacity, capacity))
|
|
|
102 |
);
|
|
|
103 |
};
|
|
|
104 |
|
|
|
105 |
Queue.prototype.maxCapacity = (1 << 30) | 0;
|
|
|
106 |
Queue.prototype.minCapacity = 16;
|
|
|
107 |
Queue.prototype.growFactor = 8;
|
|
|
108 |
|
|
|
109 |
function copy(source, sourceIndex, target, targetIndex, length) {
|
|
|
110 |
for (var index = 0; index < length; ++index) {
|
|
|
111 |
target[index + targetIndex] = source[index + sourceIndex];
|
|
|
112 |
}
|
|
|
113 |
}
|
|
|
114 |
|
|
|
115 |
function pow2AtLeast(n) {
|
|
|
116 |
n = n >>> 0;
|
|
|
117 |
n = n - 1;
|
|
|
118 |
n = n | (n >> 1);
|
|
|
119 |
n = n | (n >> 2);
|
|
|
120 |
n = n | (n >> 4);
|
|
|
121 |
n = n | (n >> 8);
|
|
|
122 |
n = n | (n >> 16);
|
|
|
123 |
return n + 1;
|
|
|
124 |
}
|
|
|
125 |
"use strict";
|
|
|
126 |
|
|
|
127 |
// Use the fastest possible means to execute a task in a future turn
|
|
|
128 |
// of the event loop.
|
|
|
129 |
|
|
|
130 |
// Queue is a circular buffer with good locality of reference and doesn't
|
|
|
131 |
// allocate new memory unless there are more than `InitialCapacity` parallel
|
|
|
132 |
// tasks in which case it will resize itself generously to x8 more capacity.
|
|
|
133 |
// The use case of asap should require no or few amount of resizes during
|
|
|
134 |
// runtime.
|
|
|
135 |
// Calling a task frees a slot immediately so if the calling
|
|
|
136 |
// has a side effect of queuing itself again, it can be sustained
|
|
|
137 |
// without additional memory
|
|
|
138 |
// Queue specifically uses
|
|
|
139 |
// http://en.wikipedia.org/wiki/Circular_buffer#Use_a_Fill_Count
|
|
|
140 |
// Because:
|
|
|
141 |
// 1. We need fast .length operation, since queue
|
|
|
142 |
// could have changed after every iteration
|
|
|
143 |
// 2. Modulus can be negated by using power-of-two
|
|
|
144 |
// capacities and replacing it with bitwise AND
|
|
|
145 |
// 3. It will not be used in a multi-threaded situation.
|
|
|
146 |
|
|
|
147 |
var Queue = require("./queue");
|
|
|
148 |
|
|
|
149 |
//1024 = InitialCapacity
|
|
|
150 |
var queue = new Queue(1024);
|
|
|
151 |
var flushing = false;
|
|
|
152 |
var requestFlush = void 0;
|
|
|
153 |
var hasSetImmediate = typeof setImmediate === "function";
|
|
|
154 |
var domain;
|
|
|
155 |
|
|
|
156 |
// Avoid shims from browserify.
|
|
|
157 |
// The existence of `global` in browsers is guaranteed by browserify.
|
|
|
158 |
var process = global.process;
|
|
|
159 |
|
|
|
160 |
// Note that some fake-Node environments,
|
|
|
161 |
// like the Mocha test runner, introduce a `process` global.
|
|
|
162 |
var isNodeJS = !!process && ({}).toString.call(process) === "[object process]";
|
|
|
163 |
|
|
|
164 |
function flush() {
|
|
|
165 |
/* jshint loopfunc: true */
|
|
|
166 |
|
|
|
167 |
while (queue.length > 0) {
|
|
|
168 |
var task = queue.shift();
|
|
|
169 |
|
|
|
170 |
try {
|
|
|
171 |
task.call();
|
|
|
172 |
|
|
|
173 |
} catch (e) {
|
|
|
174 |
if (isNodeJS) {
|
|
|
175 |
// In node, uncaught exceptions are considered fatal errors.
|
|
|
176 |
// Re-throw them to interrupt flushing!
|
|
|
177 |
|
|
|
178 |
// Ensure continuation if an uncaught exception is suppressed
|
|
|
179 |
// listening process.on("uncaughtException") or domain("error").
|
|
|
180 |
requestFlush();
|
|
|
181 |
|
|
|
182 |
throw e;
|
|
|
183 |
|
|
|
184 |
} else {
|
|
|
185 |
// In browsers, uncaught exceptions are not fatal.
|
|
|
186 |
// Re-throw them asynchronously to avoid slow-downs.
|
|
|
187 |
setTimeout(function () {
|
|
|
188 |
throw e;
|
|
|
189 |
}, 0);
|
|
|
190 |
}
|
|
|
191 |
}
|
|
|
192 |
}
|
|
|
193 |
|
|
|
194 |
flushing = false;
|
|
|
195 |
}
|
|
|
196 |
|
|
|
197 |
if (isNodeJS) {
|
|
|
198 |
// Node.js
|
|
|
199 |
requestFlush = function () {
|
|
|
200 |
// Ensure flushing is not bound to any domain.
|
|
|
201 |
var currentDomain = process.domain;
|
|
|
202 |
if (currentDomain) {
|
|
|
203 |
domain = domain || (1,require)("domain");
|
|
|
204 |
domain.active = process.domain = null;
|
|
|
205 |
}
|
|
|
206 |
|
|
|
207 |
// Avoid tick recursion - use setImmediate if it exists.
|
|
|
208 |
if (flushing && hasSetImmediate) {
|
|
|
209 |
setImmediate(flush);
|
|
|
210 |
} else {
|
|
|
211 |
process.nextTick(flush);
|
|
|
212 |
}
|
|
|
213 |
|
|
|
214 |
if (currentDomain) {
|
|
|
215 |
domain.active = process.domain = currentDomain;
|
|
|
216 |
}
|
|
|
217 |
};
|
|
|
218 |
|
|
|
219 |
} else if (hasSetImmediate) {
|
|
|
220 |
// In IE10, or https://github.com/NobleJS/setImmediate
|
|
|
221 |
requestFlush = function () {
|
|
|
222 |
setImmediate(flush);
|
|
|
223 |
};
|
|
|
224 |
|
|
|
225 |
} else if (typeof MessageChannel !== "undefined") {
|
|
|
226 |
// modern browsers
|
|
|
227 |
// http://www.nonblocking.io/2011/06/windownexttick.html
|
|
|
228 |
var channel = new MessageChannel();
|
|
|
229 |
// At least Safari Version 6.0.5 (8536.30.1) intermittently cannot create
|
|
|
230 |
// working message ports the first time a page loads.
|
|
|
231 |
channel.port1.onmessage = function () {
|
|
|
232 |
requestFlush = requestPortFlush;
|
|
|
233 |
channel.port1.onmessage = flush;
|
|
|
234 |
flush();
|
|
|
235 |
};
|
|
|
236 |
var requestPortFlush = function () {
|
|
|
237 |
// Opera requires us to provide a message payload, regardless of
|
|
|
238 |
// whether we use it.
|
|
|
239 |
channel.port2.postMessage(0);
|
|
|
240 |
};
|
|
|
241 |
requestFlush = function () {
|
|
|
242 |
setTimeout(flush, 0);
|
|
|
243 |
requestPortFlush();
|
|
|
244 |
};
|
|
|
245 |
|
|
|
246 |
} else {
|
|
|
247 |
// old browsers
|
|
|
248 |
requestFlush = function () {
|
|
|
249 |
setTimeout(flush, 0);
|
|
|
250 |
};
|
|
|
251 |
}
|
|
|
252 |
|
|
|
253 |
function asap(task) {
|
|
|
254 |
if (isNodeJS && process.domain) {
|
|
|
255 |
task = process.domain.bind(task);
|
|
|
256 |
}
|
|
|
257 |
|
|
|
258 |
queue.push(task);
|
|
|
259 |
|
|
|
260 |
if (!flushing) {
|
|
|
261 |
requestFlush();
|
|
|
262 |
flushing = true;
|
|
|
263 |
}
|
|
|
264 |
};
|
|
|
265 |
|
|
|
266 |
module.exports = asap;
|
|
|
267 |
/**
|
|
|
268 |
@module timers
|
|
|
269 |
**/
|
|
|
270 |
|
|
|
271 |
/**
|
|
|
272 |
Y.soon accepts a callback function. The callback function will be called
|
|
|
273 |
once in a future turn of the JavaScript event loop. If the function
|
|
|
274 |
requires a specific execution context or arguments, wrap it with Y.bind.
|
|
|
275 |
Y.soon returns an object with a cancel method. If the cancel method is
|
|
|
276 |
called before the callback function, the callback function won't be
|
|
|
277 |
called.
|
|
|
278 |
|
|
|
279 |
@method soon
|
|
|
280 |
@for YUI
|
|
|
281 |
@param {Function} callbackFunction
|
|
|
282 |
@return {Object} An object with a cancel method. If the cancel method is
|
|
|
283 |
called before the callback function, the callback function won't be
|
|
|
284 |
called.
|
|
|
285 |
**/
|
|
|
286 |
function soon(callbackFunction) {
|
|
|
287 |
var canceled;
|
|
|
288 |
|
|
|
289 |
soon._asynchronizer(function () {
|
|
|
290 |
// Some asynchronizers may provide their own cancellation
|
|
|
291 |
// methods such as clearImmediate or clearTimeout but some
|
|
|
292 |
// asynchronizers do not. For simplicity, cancellation is
|
|
|
293 |
// entirely handled here rather than wrapping the other methods.
|
|
|
294 |
// All asynchronizers are expected to always call this anonymous
|
|
|
295 |
// function.
|
|
|
296 |
if (!canceled) {
|
|
|
297 |
callbackFunction();
|
|
|
298 |
}
|
|
|
299 |
});
|
|
|
300 |
|
|
|
301 |
return {
|
|
|
302 |
cancel: function () {
|
|
|
303 |
canceled = 1;
|
|
|
304 |
}
|
|
|
305 |
};
|
|
|
306 |
}
|
|
|
307 |
|
|
|
308 |
soon._asynchronizer = asap;
|
|
|
309 |
soon._impl = 'asap';
|
|
|
310 |
|
|
|
311 |
Y.soon = soon;
|
|
|
312 |
|
|
|
313 |
|
|
|
314 |
}, '3.18.1', {"requires": ["yui-base"]});
|