Skip to content

Commit ec311a6

Browse files
committed
Initial commit
0 parents  commit ec311a6

31 files changed

+17719
-0
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
node_modules

.npmignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
.git*
2+
.npmignore
3+
node_modules/

.travis.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
language: node_js
2+
node_js:
3+
- 0.8

LICENSE

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
Copyright (c) 2014 Ferdinand Prantl <[email protected]>
2+
3+
Permission is hereby granted, free of charge, to any person
4+
obtaining a copy of this software and associated documentation
5+
files (the "Software"), to deal in the Software without
6+
restriction, including without limitation the rights to use,
7+
copy, modify, merge, publish, distribute, sublicense, and/or sell
8+
copies of the Software, and to permit persons to whom the
9+
Software is furnished to do so, subject to the following
10+
conditions:
11+
12+
The above copyright notice and this permission notice shall be
13+
included in all copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
17+
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
19+
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
20+
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21+
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22+
OTHER DEALINGS IN THE SOFTWARE.

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
AsyncTaskQueue.js is a worker (function) queue executing the tasks asynchronously, which uses (deferred) promises to notify about the execution status.

asynctaskqueue-min.js

Lines changed: 9 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

asynctaskqueue-min.map

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

asynctaskqueue.js

Lines changed: 331 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,331 @@
1+
// asynctaskqueue.js 1.0.0
2+
// https://github.com/prantlf/asynctaskqueue.js
3+
// (c) 2014 Ferdinand Prantl <[email protected]>
4+
// Freely distributable under the MIT license
5+
6+
// ## Environment Detection
7+
8+
// Immediate fuction which gathers dependencies, calls the implementation
9+
// factory and exposes the `AsyncTaskQueue` object according to the
10+
// JavaScript environment. Underscore.js is required for the ECMAScript5
11+
// compatibility in older web browsers and JQuery is required for the
12+
// support of (deferred) promises.
13+
(function (window, _, jQuery, factory) {
14+
15+
// Variables to support non-conflicting loading in the web browser.
16+
var OldAsyncTaskQueue, AsyncTaskQueue;
17+
18+
// Register the Queue object in the AMD environment, depending
19+
// on Underscore.js and jQuery.
20+
if (typeof define !== "undefined") {
21+
define("asynctaskqueue", [ "underscrore", "jquery" ], function (_, $) {
22+
// Return the Queue object as the module export.
23+
return factory(_, $);
24+
});
25+
26+
// Detect a CommonJS module and ask for Underscore.js and jQuery.
27+
} else if (typeof module !== "undefined" &&
28+
typeof module.exports !== "undefined") {
29+
_ = require("underscore");
30+
jQuery = require("jquery");
31+
// Expose the Queue object as the only module export.
32+
module.exports = factory(_, jQuery);
33+
34+
// Detect the web browser and check that Underscore.js and jQuery were
35+
// included before ths script.
36+
} else if (typeof window !== "undefined") {
37+
if (typeof _ === "undefined") {
38+
throw new Error("Underscore.js not detected.")
39+
}
40+
if (typeof jQuery === "undefined") {
41+
throw new Error("JQuery not detected.")
42+
}
43+
// Expose the Queue object globally and give the user a chance to restore
44+
// the global namespace by calling the noConflict function.
45+
OldAsyncTaskQueue = window.AsyncTaskQueue;
46+
window.AsyncTaskQueue = AsyncTaskQueue = factory(_, jQuery);
47+
48+
// ### Non-conflicting Integration
49+
50+
// Restores the global value of `AsyncTaskQueue` which existed before
51+
// this script was loaded and returns the `AsyncTaskQueue` exported by
52+
// this script so that it can be stored in a local variable.
53+
AsyncTaskQueue.noConflict = function () {
54+
window.AsyncTaskQueue = OldAsyncTaskQueue;
55+
return AsyncTaskQueue;
56+
}
57+
58+
// No way to expose the public objects without knowing where we are.
59+
} else {
60+
throw new Error("JavaScript environment not recognized.")
61+
}
62+
63+
// ## Module Implementation
64+
65+
// Passes dependencies from the global environment and the factory function
66+
// encapsulating the `AsyncTaskQueue` implementation to the immediate
67+
// initialization function.
68+
}(window, _, jQuery, function (_, $) {
69+
70+
// ### Task Wrapper
71+
72+
// Represents a pending task in the queue.
73+
//
74+
// The _worker_ is a (function) callback to execute, which must return
75+
// a promise and reject it with an Error or just throw in case of a
76+
// failure.
77+
// The _options_ is an optional parameter passed to `Queue.enqueue`.
78+
function Task(worker, options) {
79+
this.worker = worker;
80+
this.deferred = $.Deferred();
81+
}
82+
83+
_.extend(Task.prototype, {
84+
85+
// Returns a promise for the result of the task worker.
86+
// When the task is scheduled for execution, the promise `progress` is
87+
// triggered with `{ scheduled: true }`. Other states are relayed from
88+
// the promise returned by the worker.
89+
promise: function () {
90+
return this.deferred.promise();
91+
}
92+
93+
});
94+
95+
// ### Task Scheduler
96+
97+
// Provides a FIFO queue scheduler pushing and popping pending tasks.
98+
//
99+
// The _options_ is an optional parameter passed to `Queue.constructor`.
100+
function Scheduler(options) {}
101+
102+
_.extend(Scheduler.prototype, {
103+
104+
// Pushes the task to the end of the array to be picked as the last one.
105+
push: function (tasks, task) {
106+
tasks.push(task);
107+
},
108+
109+
// Pops the (oldest) task from the beginning of the array and returns it.
110+
// Returns `undefined` if the array is empty.
111+
pop: function (tasks) {
112+
return tasks.shift();
113+
}
114+
115+
});
116+
117+
// ### Task Queue
118+
119+
// Provides a queue executing tasks.
120+
//
121+
// The _options_ is an object with the following properties:
122+
// * parallelism - number of tasks run in parallel (`Infinity` by default)
123+
// * asynchronous - boolean to schedule the task execution at the next
124+
// VM context switch (`false` by default)
125+
// * Task - object to wrap the workers with (`Task` by default)
126+
// * Scheduler - object to push pending workers to the queue and pop
127+
// them for execution later (`Scheduler` by default)
128+
// * scheduler - the optional parameter to be passed to the
129+
// `Scheduler.constructor`
130+
function Queue(options) {
131+
options || (options = {});
132+
this.parallelism = options.parallelism || Infinity;
133+
this.asynchronous = options.asynchronous;
134+
if (options.Task) {
135+
this.Task = options.Task;
136+
}
137+
if (options.Scheduler) {
138+
this.Scheduler = options.Scheduler;
139+
}
140+
this.pending = [];
141+
this.processing = [];
142+
this.deferred = $.Deferred();
143+
this.scheduler = new Scheduler(this, options.scheduler);
144+
}
145+
146+
_.extend(Queue.prototype, {
147+
148+
// Wraps the worker functions passed to `enqueue` to be manageable.
149+
Task: Task,
150+
151+
// Decides how the tasks are pushed to the pending array and in which
152+
// order they are popped for execution.
153+
Scheduler: Scheduler,
154+
155+
// #### Public Methods
156+
157+
// Adds a task to the queue.
158+
//
159+
// The _task_ is a `Task` object or a (function) callback returning a
160+
// promise to be wrapped by a `Task` instance.
161+
// The _options_ is an optional parameter to be passed to the
162+
// `Task.constructor`.
163+
//
164+
// Returns the `Task` object added to the queue.
165+
enqueue: function (task, options) {
166+
if (!(task instanceof this.Task)) {
167+
task = new this.Task(task, options);
168+
}
169+
this.scheduler.push(this.pending, task);
170+
this._schedule();
171+
return task;
172+
},
173+
174+
// Removes a task from the queue if it is still pending.
175+
//
176+
// The _task_ is a `Task` object or a (function) callback added to the
177+
// queue earlier.
178+
//
179+
// Returns the `Task` object removed from the queue or `undefined`
180+
// if no matching one was found.
181+
dequeue: function (task) {
182+
var index = this._index(this.pending, task);
183+
if (index >= 0) {
184+
return this.pending.splice(index, 1)[0];
185+
}
186+
},
187+
188+
// Returns a promise for watching the queue state. A progress
189+
// notification is triggered when a task is scheduled for execution.
190+
// The promise is resolved when all tasks have been resolved or rejected.
191+
// When it happens, the underlying `deferred` object is replaced with a
192+
// new one to serve another queue execution. Subscribers must re-attach
193+
// to the new the promise again if they want to be notified about the
194+
// other queue execution.
195+
promise: function () {
196+
return this.deferred.promise();
197+
},
198+
199+
// Returns if the queue has no tasks in pending and processing queues.
200+
empty: function () {
201+
return !(this.pending.length || this.processing.length);
202+
},
203+
204+
// Pauses scheduling of pending tasks for execution; currently executing
205+
// tasks will still continue running.
206+
//
207+
// Returns this instance for call chaining.
208+
pause: function () {
209+
this.paused = true;
210+
return this;
211+
},
212+
213+
// Resumes scheduling of pending tasks for execution.
214+
//
215+
// Returns this instance for call chaining.
216+
resume: function () {
217+
this.paused = false;
218+
// When a task execution finishes, another one is scheduled. But the
219+
// pause could be so long that all tasks have been long finished.
220+
this._schedule();
221+
return this;
222+
},
223+
224+
// Clears the pending tasks in the queue; already executing tasks will
225+
// continue running.
226+
//
227+
// Returns a promise to be resolved when the executing tasks have
228+
// finished and the queue has become empty. *Warning:* the queue promise
229+
// cannot not be used for checking for the abortion status if the queue
230+
// has been already empty; it wait for another queue run.
231+
abort: function () {
232+
this.pending = [];
233+
return this.processing.length ? this.promise() :
234+
$.Deferred.resolve().promise();
235+
},
236+
237+
// #### Private Methods
238+
239+
// Schedules another task for executing if the queue is not paused
240+
// and the maximum degree of parallelism has not been reached yet.
241+
_schedule: function () {
242+
var task;
243+
if (!this.paused &&
244+
this.processing.length < this.parallelism &&
245+
(task = this.scheduler.pop(this.pending))) {
246+
this.processing.push(task);
247+
this.deferred.notify({ scheduled: true });
248+
this._execute(task);
249+
// Do not wait until the task execution has finished; the queue may
250+
// be configured to allow parallel task execution.
251+
this._schedule();
252+
}
253+
},
254+
255+
// Calls the worker function of the task and relays its promise.
256+
_execute: function (task) {
257+
var self = this;
258+
// Extracted to a local function to be callable by various ways.
259+
function work() {
260+
try {
261+
task.worker()
262+
.progress(task.deferred.notify)
263+
.done(task.deferred.done)
264+
.fail(task.deferred.fail)
265+
.always(function () {
266+
self._finish(task);
267+
});
268+
// Tasks should singnal error through their promise, but if they meet
269+
// a fatal failure, the queue execution should not freeze.
270+
} catch (error) {
271+
task.deferred.reject(error);
272+
self._finish(task);
273+
}
274+
}
275+
if (this.asynchronous) {
276+
// `setTimeout(..., 0)` makes the callback wait 4 ms before it can be
277+
// executed but `setImmediate` is not available in most web browsers.
278+
if (typeof setImmediate !== "undefined") {
279+
setImmediate(work);
280+
} else {
281+
setTimeout(work, 0);
282+
}
283+
} else {
284+
// If the workers do asynchronous operations like HTTP requests, the
285+
// extra asynchronous worker execution may not be necessary.
286+
work();
287+
}
288+
},
289+
290+
// Called when a task finished its execution to update the queue state.
291+
_finish: function (task) {
292+
// Remove the task from the processing array; it will leave the queue.
293+
var index = this._index(this.processing, task);
294+
this.processing.splice(index, 1);
295+
// When new tasks are added after the queue was emptied, it is
296+
// considered a new queue execution; the once notified watchers should
297+
// not be informed again. Once signalled promise cannot be reset either.
298+
if (this.empty()) {
299+
this.deferred.resolve();
300+
this.deferred = $.Deferred();
301+
} else {
302+
// If more tasks than the allowed degree of parallelism have been
303+
// added to the queue, schedule anoher one now.
304+
this._schedule();
305+
}
306+
},
307+
308+
// Returns the index of the task in the specified array or -1 if no
309+
// matching one was found. It accepts both `Task` objects and worker
310+
// functions to identify the task.
311+
_index: function (tasks, task) {
312+
var i;
313+
if (task instanceof this.Task) {
314+
task = task.worker;
315+
}
316+
for (i = 0; i < tasks.length; ++i) {
317+
if (tasks[i].worker === task) {
318+
return i;
319+
}
320+
}
321+
return -1;
322+
}
323+
324+
});
325+
326+
Queue.version = "1.0.0";
327+
328+
return Queue;
329+
330+
}));
331+

bower.json

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"name" : "asynctaskqueue.js",
3+
"version" : "1.0.0",
4+
"main" : "asynctaskqueue.js",
5+
"keywords" : ["worker", "queue", "asynchronous", "server", "client", "browser"],
6+
"ignore" : ["docs", "test", "*.yml", "*.map"]
7+
}

component.json

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
{
2+
"name" : "asynctaskqueue.js",
3+
"description" : "JavaScript worker queue to execute tasks asynchronously, based on (deferred) promises",
4+
"keywords" : ["worker", "queue", "asynchronous", "server", "client", "browser"],
5+
"repo" : "prantlf/asynctaskqueue.js",
6+
"main" : "asynctaskqueue.js",
7+
"scripts" : ["asynctaskqueue.js"],
8+
"version" : "1.0.0",
9+
"license" : "MIT"
10+
}

0 commit comments

Comments
 (0)