Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

v3.3.0. Improvement

  • Loading branch information...
commit b83899ba52cad23a1a92ed744032fb09276e5c24 1 parent 742b4b4
@balupton balupton authored
View
5 .gitignore
@@ -1,4 +1,4 @@
-# v1.3.0 October 26, 2013
+# v1.3.5 October 31, 2013
# https://github.com/bevry/base
# Temp Files
@@ -8,4 +8,5 @@
build/
components/
bower_components/
-node_modules/
+node_modules/
+out/
View
8 Cakefile
@@ -1,4 +1,4 @@
-# v1.3.1 October 26, 2013
+# v1.3.4 October 30, 2013
# https://github.com/bevry/base
@@ -138,7 +138,11 @@ actions =
# npm publish
spawn(NPM, ['publish'], {stdio:'inherit', cwd:APP_DIR}).on 'close', safe next, ->
# git tag
- spawn(GIT, ['tag', 'v'+PACKAGE_DATA.version, '-a'], {stdio:'inherit', cwd:APP_DIR}).on('close', safe next)
+ spawn(GIT, ['tag', 'v'+PACKAGE_DATA.version, '-a'], {stdio:'inherit', cwd:APP_DIR}).on 'close', safe next, ->
+ # git push origin master
+ spawn(GIT, ['push', 'origin', 'master'], {stdio:'inherit', cwd:APP_DIR}).on 'close', safe next, ->
+ # git push tags
+ spawn(GIT, ['push', 'origin', '--tags'], {stdio:'inherit', cwd:APP_DIR}).on('close', safe next)
# =====================================
View
6 History.md
@@ -1,5 +1,11 @@
## History
+- v3.3.0 November 1, 2013
+ - Bindings are now more explicit
+ - Improved configuration parsing
+ - Configuration is now accessed via `getConfig()`
+ - Dropped component.io and bower support, just use ender or browserify
+
- v3.2.4 October 27, 2013
- Re-packaged
View
3  LICENSE.md
@@ -3,7 +3,8 @@
# License
-Copyright &copy; Benjamin Lupton <b@lupton.cc>
+Copyright &copy; 2013+ Bevry Pty Ltd <us@bevry.me> (http://bevry.me)
+<br/>Copyright &copy; 2011-2012 Benjamin Lupton <b@lupton.cc> (http://balupton.com)
## The MIT License
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the 'Software'), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
View
138 README.md
@@ -9,10 +9,10 @@
<!-- BADGES/ -->
[![Build Status](http://img.shields.io/travis-ci/bevry/taskgroup.png?branch=master)](http://travis-ci.org/bevry/taskgroup "Check this project's build status on TravisCI")
-[![NPM version](https://badge.fury.io/js/taskgroup.png)](https://npmjs.org/package/taskgroup "View this project on NPM")
+[![NPM version](http://badge.fury.io/js/taskgroup.png)](https://npmjs.org/package/taskgroup "View this project on NPM")
[![Gittip donate button](http://img.shields.io/gittip/bevry.png)](https://www.gittip.com/bevry/ "Donate weekly to this project using Gittip")
-[![Flattr donate button](https://raw.github.com/balupton/flattr-buttons/master/badge-89x18.gif)](http://flattr.com/thing/344188/balupton-on-Flattr "Donate monthly to this project using Flattr")
-[![PayPayl donate button](https://www.paypalobjects.com/en_AU/i/btn/btn_donate_SM.gif)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=QB8GQPZAH84N6 "Donate once-off to this project using Paypal")
+[![Flattr donate button](http://img.shields.io/flattr/donate.png?color=yellow)](http://flattr.com/thing/344188/balupton-on-Flattr "Donate monthly to this project using Flattr")
+[![PayPayl donate button](http://img.shields.io/paypal/donate.png?color=yellow)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=QB8GQPZAH84N6 "Donate once-off to this project using Paypal")
<!-- /BADGES -->
@@ -36,14 +36,6 @@ Group together synchronous and asynchronous tasks and execute them with support
- Use: `require('taskgroup')`
- Install: `ender add taskgroup`
-### [Component](http://github.com/component/component)
-- Use: `require('taskgroup')`
-- Install: `component install bevry/taskgroup`
-
-### [Bower](http://bower.io/)
-- Use: `require('taskgroup')`
-- Install: `bower install taskgroup`
-
<!-- /INSTALL -->
@@ -59,7 +51,7 @@ var TaskGroup = require('taskgroup').TaskGroup;
var group = new TaskGroup();
// Define what should happen once the group has completed
-group.once('complete', function(err,results){
+group.once('complete', function(err, results){
// Log the error that has occured
console.log(err);
// => null
@@ -92,7 +84,7 @@ group.addTask(function(){
});
// Add a sub-group to our exiting group
-group.addGroup(function(addGroup,addTask){
+group.addGroup(function(addGroup, addTask){
// Tell this sub-group to execute in parallel (all at once) by setting its concurrency to unlimited
// by default the concurrency for all groups is set to 1
// which means that they execute in serial fashion (one after the other, instead of all at once)
@@ -122,10 +114,12 @@ new (require('taskgroup')).TaskGroup()
```
- Available methods:
- - `constructor(name?,fn?)` - create our new group, the arguments `name` and `fn` are optional, refer to their entries in configuration
+ - `constructor(name?,fn?)` - create our new group, arguments can be a String for `name`, an Object for `config`, and a Function for `next`
- `setConfig(config)` - set the configuration for the group, returns chain
- - `addTask(args...)` - create a new task item with the arguments and adds it to the group, returns the new task item
- - `addGroup(args...)` - create a new group item with the arguments and adds it to the group, returns the new group item
+ - `getconfig()` - return the set configuration
+ - `addTask(args...)`, `addTasks(tasks, args..)` - create a new task item with the arguments and adds it to the group, returns the new task item(s)
+ - `addGroup(args...)`, `addGroups(groups, args..)` - create a new group item with the arguments and adds it to the group, returns the new group item(s)
+ - `addItem(item)`, `addItem(items)` - adds the items to the group, returns the item(s)
- `getTotals()` - returns counts for the following `{running,remaining,completed,total}`
- `clear()` - remove the remaining items to be executed
- `pause()` - pause the execution of the items
@@ -136,10 +130,14 @@ new (require('taskgroup')).TaskGroup()
- All those of [EventEmitter2](https://github.com/hij1nx/EventEmitter2)
- Available configuration:
- `name`, no default - allows us to assign a name to the group, useful for debugging
- - `fn(addGroup,addTask,complete?)`, no default - allows us to use an inline and self-executing style for defining groups, useful for nesting
+ - `method(addGroup, addTask, complete?)`, no default - allows us to use an inline and self-executing style for defining groups, useful for nesting
- `concurrency`, defaults to `1` - how many items shall we allow to be run at the same time, set to `0` to allow unlimited
- `pauseOnError`, defaults to `true` - if an error occurs in one of our items, should we stop executing any remaining items?
- setting to `false` will continue with execution with the other items even if an item experiences an error
+ - `items` - alias for `.addTasks(items)`
+ - `groups` - alias for `.addGroups(groups)`
+ - `tasks` - alias for `.addTasks(tasks)`
+ - `next` - alias for `.once('complete', next)`
- Available events:
- `run()` - fired just before we execute the items
- `complete(err, results)` - fired when all our items have completed
@@ -158,20 +156,21 @@ new (require('taskgroup')).Task()
```
- Available methods:
- - `constructor(name?,fn?)` - create our new task, the arguments `name` and `fn` are optional though `fn` must be set at some point, refer to their entries in configuration
+ - `constructor(args...)` - create our new task, arguments can be a String for `name`, an Object for `config`, and a Function for `next`
- `setConfig(config)` - set the configuration for the group, returns chain
+ - `getconfig()` - return the set configuration
- `complete()` - will fire the completion event if we are already complete, useful if you're binding your listeners after run
- `run()` - execute the task
- Available configuration:
- `name`, no default - allows us to assign a name to the group, useful for debugging
- - `fn(complete?)`, no default - must be set at some point, it is the function to execute for the task, if it is asynchronous it should use the completion callback provided
+ - `method(complete?)`, no default - must be set at some point, it is the function to execute for the task, if it is asynchronous it should use the completion callback provided
- `args`, no default - an array of arguments that you would like to precede the completion callback when executing `fn`
+ - `next` - alias for `.once('complete', next)`
- Available events:
- `run()` - fired just before we execute the task
- `complete(err, args...)` - fired when the task has completed
-
## Comparison with [Async.js](https://github.com/caolan/async)
The biggest advantage and difference of TaskGroup over async.js is that TaskGroup has one uniform API to rule them all, whereas with async.js I found that I was always having to keep referring to the async manual to try and figure out which is the right call for my use case then somehow wrap my head around the async.js way of doing things (which more often than not I couldn't), whereas with TaskGroup I never have that problem as it is one consistent API for all the different use cases.
@@ -189,10 +188,13 @@ async.series([
], next);
// TaskGroup
-new TaskGroup().once('complete', next)
- .addTask(function(){})
- .addTask(function(callback){callback();})
- .run();
+new TaskGroup({
+ tasks: [
+ function(){},
+ function(callback){callback();}
+ ],
+ next: next
+}).run();
// ====================================
@@ -205,11 +207,14 @@ async.parallel([
], next);
// TaskGroup
-new TaskGroup().setConfig({concurrency:0}).once('complete', next)
- .addTask(function(){})
- .addTask(function(callback){callback();})
- .run();
-
+new TaskGroup({
+ concurrency: 0,
+ tasks: [
+ function(){},
+ function(callback){callback();}
+ ],
+ next: next
+}).run();
// ====================================
// Map
@@ -218,13 +223,15 @@ new TaskGroup().setConfig({concurrency:0}).once('complete', next)
async.map(['file1','file2','file3'], fs.stat, next);
// TaskGroup
-var tasks = new TaskGroup().setConfig({concurrency:0}).once('complete', next);
-['file1','file2','file3'].forEach(function(file){
- tasks.addTask(function(complete){
- fs.stat(file,complete);
- });
-});
-tasks.run();
+new TaskGroup({
+ concurrency: 0,
+ tasks: ['file1', 'file2', 'file3'].map(function(file){
+ return function(complete){
+ fs.stat(file, complete);
+ }
+ }),
+ next: next
+}).run();
```
Another big advantage of TaskGroup over async.js is TaskGroup's ability to add tasks to the group once execution has already started - this is a common use case when creating an application that must perform it's actions serially, so using TaskGroup you can create a serial TaskGroup for the application, run it right away, then add the actions to the group as tasks.
@@ -232,11 +239,62 @@ Another big advantage of TaskGroup over async.js is TaskGroup's ability to add t
A final big advantage of TaskGroup over async.js is TaskGroup's ability to do nested groups, this allowed us to created the [Joe Testing Framework & Runner](https://github.com/bevry/joe) incredibly easily, and because of this functionality Joe will always know which test (task) is associated to which suite (task group), whereas test runners like mocha have to guess (they add the task to the last group, which may not always be the case! especially with dynamically created tests!).
+<!-- HISTORY/ -->
+
## History
-You can discover the history inside the [History.md](https://github.com/bevry/taskgroup/blob/master/History.md#files) file
+[Discover the change history by heading on over to the `History.md` file.](https://github.com/bevry/taskgroup/blob/master/History.md#files)
+
+<!-- /HISTORY -->
+
+
+<!-- CONTRIBUTE/ -->
+
+## Contribute
+
+[Discover how you can contribute by heading on over to the `Contributing.md` file.](https://github.com/bevry/taskgroup/blob/master/Contributing.md#files)
+
+<!-- /CONTRIBUTE -->
+
+
+<!-- BACKERS/ -->
+
+## Backers
+### Maintainers
+
+These amazing people are maintaining this project:
+
+- Benjamin Lupton <b@lupton.cc> (https://github.com/balupton)
+
+### Sponsors
+
+No sponsors yet! Will you be the first?
+
+[![Gittip donate button](http://img.shields.io/gittip/bevry.png)](https://www.gittip.com/bevry/ "Donate weekly to this project using Gittip")
+[![Flattr donate button](http://img.shields.io/flattr/donate.png?color=yellow)](http://flattr.com/thing/344188/balupton-on-Flattr "Donate monthly to this project using Flattr")
+[![PayPayl donate button](http://img.shields.io/paypal/donate.png?color=yellow)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=QB8GQPZAH84N6 "Donate once-off to this project using Paypal")
+
+### Contributors
+
+These amazing people have contributed code to this project:
+
+- Benjamin Lupton <b@lupton.cc> (https://github.com/balupton) - [view contributions](https://github.com/bevry/taskgroup/commits?author=balupton)
+- sfrdmn (https://github.com/sfrdmn) - [view contributions](https://github.com/bevry/taskgroup/commits?author=sfrdmn)
+
+[Become a contributor!](https://github.com/bevry/taskgroup/blob/master/Contributing.md#files)
+
+<!-- /BACKERS -->
+
+
+<!-- LICENSE/ -->
## License
-Licensed under the incredibly [permissive](http://en.wikipedia.org/wiki/Permissive_free_software_licence) [MIT License](http://creativecommons.org/licenses/MIT/)
-<br/>Copyright © 2013+ [Bevry Pty Ltd](http://bevry.me)
-<br/>Copyright © 2011-2012 [Benjamin Arthur Lupton](http://balupton.com)
+
+Licensed under the incredibly [permissive](http://en.wikipedia.org/wiki/Permissive_free_software_licence) [MIT license](http://creativecommons.org/licenses/MIT/)
+
+Copyright &copy; 2013+ Bevry Pty Ltd <us@bevry.me> (http://bevry.me)
+<br/>Copyright &copy; 2011-2012 Benjamin Lupton <b@lupton.cc> (http://balupton.com)
+
+<!-- /LICENSE -->
+
+
View
37 bower.json
@@ -1,37 +0,0 @@
-{
- "name": "taskgroup",
- "version": "3.2.4",
- "homepage": "https://github.com/bevry/taskgroup",
- "authors": [
- "Benjamin Lupton <b@lupton.cc>"
- ],
- "description": "Group together synchronous and asynchronous tasks and execute them with support for concurrency, naming, and nesting.",
- "main": "./out/lib/taskgroup.js",
- "keywords": [
- "flow",
- "control",
- "async",
- "sync",
- "tasks",
- "batch",
- "concurrency"
- ],
- "license": "MIT",
- "ignore": [
- "**/.*",
- "node_modules",
- "bower_components",
- "test",
- "tests"
- ],
- "dependencies": {
- "ambi": "~2.1.5"
- },
- "devDependencies": {
- "coffee-script": "~1.6.2",
- "joe": "~1.3.0",
- "joe-reporter-console": "~1.2.1",
- "chai": "~1.8.1",
- "projectz": "~0.2.3"
- }
-}
View
27 component.json
@@ -1,27 +0,0 @@
-{
- "name": "taskgroup",
- "repo": "bevry/taskgroup",
- "description": "Group together synchronous and asynchronous tasks and execute them with support for concurrency, naming, and nesting.",
- "version": "3.2.4",
- "keywords": [
- "flow",
- "control",
- "async",
- "sync",
- "tasks",
- "batch",
- "concurrency"
- ],
- "dependencies": {
- "bevry/ambi": "master",
- "bevry/domain-browser": "master",
- "bevry/events-browser": "master"
- },
- "development": {},
- "license": "MIT",
- "main": "./out/lib/taskgroup.js",
- "scripts": [
- "./out/lib/taskgroup.js"
- ],
- "demo": "https://github.com/bevry/taskgroup"
-}
View
20 cyclic.js
@@ -1,9 +1,13 @@
-// v1.3.0 October 26, 2013
+// v1.3.7 November 1, 2013
// https://github.com/bevry/base
-if ( require('fs').existsSync('.git') ) {
- require('child_process').spawn(
- process.platform.indexOf('win') === 0 ? process.execPath.replace('node.exe', 'npm.cmd') : 'npm',
- ['install', '--force', require('./package.json').name],
- {env:process.env, cwd:process.cwd(), stdio:'inherit'}
- ).on('error', console.log).on('close', console.log);
-}
+(function(){
+ var fsUtil = require('fs'),
+ name = require('./package.json').name;
+ if ( fsUtil.existsSync('.git') === true && fsUtil.existsSync('./node_modules/'+name) === false ) {
+ require('child_process').spawn(
+ process.platform.indexOf('win') === 0 ? process.execPath.replace('node.exe', 'npm.cmd') : 'npm',
+ ['install', '--force', name],
+ {env:process.env, cwd:process.cwd(), stdio:'inherit'}
+ ).on('error', console.log).on('close', console.log);
+ }
+})()
View
467 out/lib/taskgroup.js
@@ -1,467 +0,0 @@
-// Generated by CoffeeScript 1.6.3
-(function() {
- var EventEmitter, Task, TaskGroup, ambi, domain, events,
- __hasProp = {}.hasOwnProperty,
- __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; },
- __slice = [].slice,
- __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };
-
- ambi = require('ambi');
-
- events = typeof window !== "undefined" && window !== null ? require('events-browser') : require('events');
-
- domain = typeof window !== "undefined" && window !== null ? require('domain-browser') : require('domain');
-
- EventEmitter = events.EventEmitter;
-
- Task = (function(_super) {
- __extends(Task, _super);
-
- Task.prototype.type = 'task';
-
- Task.prototype.result = null;
-
- Task.prototype.running = false;
-
- Task.prototype.completed = false;
-
- Task.prototype.parent = null;
-
- Task.prototype.taskDomain = null;
-
- Task.prototype.name = null;
-
- Task.prototype.method = null;
-
- Task.prototype.args = null;
-
- function Task() {
- var args, method, name;
- args = 1 <= arguments.length ? __slice.call(arguments, 0) : [];
- Task.__super__.constructor.apply(this, arguments);
- name = method = null;
- if (args.length) {
- if (args.length === 2) {
- name = args[0], method = args[1];
- } else if (args.length === 1) {
- method = args[0];
- }
- }
- this.setConfig({
- name: name,
- method: method
- });
- this;
- }
-
- Task.prototype.setConfig = function(opts) {
- var key, value;
- if (opts == null) {
- opts = {};
- }
- for (key in opts) {
- if (!__hasProp.call(opts, key)) continue;
- value = opts[key];
- this[key] = value;
- }
- return this;
- };
-
- Task.prototype.reset = function() {
- this.completed = false;
- this.running = false;
- this.result = null;
- return this;
- };
-
- Task.prototype.uncaughtExceptionCallback = function() {
- var args, err;
- args = 1 <= arguments.length ? __slice.call(arguments, 0) : [];
- err = args[0];
- if (!this.completed) {
- this.complete(args);
- }
- this.emit('error', err);
- return this;
- };
-
- Task.prototype.completionCallback = function() {
- var args, err;
- args = 1 <= arguments.length ? __slice.call(arguments, 0) : [];
- if (!this.completed) {
- this.complete(args);
- this.emit.apply(this, ['complete'].concat(__slice.call(this.result)));
- } else {
- err = new Error("A task's completion callback has fired when the task was already in a completed state, this is unexpected");
- this.emit('error', err);
- }
- return this;
- };
-
- Task.prototype.destroy = function() {
- this.removeAllListeners();
- return this;
- };
-
- Task.prototype.complete = function(result) {
- this.completed = true;
- this.running = false;
- this.result = result;
- return this;
- };
-
- Task.prototype.fire = function() {
- var args,
- _this = this;
- args = (this.args || []).concat([this.completionCallback.bind(this)]);
- if (this.taskDomain == null) {
- this.taskDomain = domain.create();
- this.taskDomain.on('error', this.uncaughtExceptionCallback.bind(this));
- }
- this.taskDomain.run(function() {
- var err;
- try {
- return ambi.apply(null, [_this.method.bind(_this)].concat(__slice.call(args)));
- } catch (_error) {
- err = _error;
- return _this.uncaughtExceptionCallback(err);
- }
- });
- return this;
- };
-
- Task.prototype.run = function() {
- var err;
- if (this.completed) {
- err = new Error("A task was about to run but it has already completed, this is unexpected");
- this.emit('error', err);
- } else {
- this.reset();
- this.running = true;
- this.emit('run');
- process.nextTick(this.fire.bind(this));
- }
- return this;
- };
-
- return Task;
-
- })(EventEmitter);
-
- TaskGroup = (function(_super) {
- __extends(TaskGroup, _super);
-
- TaskGroup.prototype.type = 'taskgroup';
-
- TaskGroup.prototype.running = 0;
-
- TaskGroup.prototype.remaining = null;
-
- TaskGroup.prototype.err = null;
-
- TaskGroup.prototype.results = null;
-
- TaskGroup.prototype.parent = null;
-
- TaskGroup.prototype.paused = true;
-
- TaskGroup.prototype.bubbleEvents = null;
-
- TaskGroup.prototype.name = null;
-
- TaskGroup.prototype.method = null;
-
- TaskGroup.prototype.concurrency = 1;
-
- TaskGroup.prototype.pauseOnError = true;
-
- function TaskGroup() {
- var args, me, method, name;
- args = 1 <= arguments.length ? __slice.call(arguments, 0) : [];
- this.addGroup = __bind(this.addGroup, this);
- this.addTask = __bind(this.addTask, this);
- me = this;
- TaskGroup.__super__.constructor.apply(this, arguments);
- if (this.results == null) {
- this.results = [];
- }
- if (this.remaining == null) {
- this.remaining = [];
- }
- if (this.bubbleEvents == null) {
- this.bubbleEvents = ['complete', 'run', 'error'];
- }
- name = method = null;
- if (args.length) {
- if (args.length === 2) {
- name = args[0], method = args[1];
- } else if (args.length === 1) {
- method = args[0];
- }
- }
- this.setConfig({
- name: name,
- method: method
- });
- process.nextTick(this.fire.bind(this));
- this.on('item.complete', this.itemCompletionCallback.bind(this));
- this.on('item.error', function(item, err) {
- me.stop();
- return me.emit('error', err);
- });
- this;
- }
-
- TaskGroup.prototype.setConfig = function(opts) {
- var key, value;
- if (opts == null) {
- opts = {};
- }
- for (key in opts) {
- if (!__hasProp.call(opts, key)) continue;
- value = opts[key];
- this[key] = value;
- }
- return this;
- };
-
- TaskGroup.prototype.fire = function() {
- var args;
- if (this.method) {
- args = [this.addGroup, this.addTask];
- this.addTask(this.method.bind(this)).setConfig({
- args: args,
- includeInResults: false
- });
- if (!this.parent) {
- this.run();
- }
- }
- return this;
- };
-
- TaskGroup.prototype.itemCompletionCallback = function() {
- var args, item;
- item = arguments[0], args = 2 <= arguments.length ? __slice.call(arguments, 1) : [];
- if (item.includeInResults !== false) {
- this.results.push(args);
- }
- if (args[0]) {
- this.err = args[0];
- }
- if (this.running > 0) {
- --this.running;
- }
- if (this.paused) {
- return;
- }
- if (!this.complete()) {
- this.nextItems();
- }
- return this;
- };
-
- TaskGroup.prototype.getTotals = function() {
- var completed, remaining, running, total;
- running = this.running;
- remaining = this.remaining.length;
- completed = this.results.length;
- total = running + remaining + completed;
- return {
- running: running,
- remaining: remaining,
- completed: completed,
- total: total
- };
- };
-
- TaskGroup.prototype.addItem = function(item) {
- var me;
- me = this;
- this.bubbleEvents.forEach(function(bubbleEvent) {
- return item.on(bubbleEvent, function() {
- var args;
- args = 1 <= arguments.length ? __slice.call(arguments, 0) : [];
- return me.emit.apply(me, ["item." + bubbleEvent, item].concat(__slice.call(args)));
- });
- });
- this.emit('item.add', item);
- this.remaining.push(item);
- if (!this.paused) {
- this.nextItems();
- }
- return item;
- };
-
- TaskGroup.prototype.createTask = function() {
- var args, task;
- args = 1 <= arguments.length ? __slice.call(arguments, 0) : [];
- task = (function(func, args, ctor) {
- ctor.prototype = func.prototype;
- var child = new ctor, result = func.apply(child, args);
- return Object(result) === result ? result : child;
- })(Task, args, function(){});
- return task;
- };
-
- TaskGroup.prototype.addTask = function() {
- var args, me, task;
- args = 1 <= arguments.length ? __slice.call(arguments, 0) : [];
- me = this;
- task = this.createTask.apply(this, args).setConfig({
- parent: this
- });
- this.bubbleEvents.forEach(function(bubbleEvent) {
- return task.on(bubbleEvent, function() {
- var args;
- args = 1 <= arguments.length ? __slice.call(arguments, 0) : [];
- return me.emit.apply(me, ["task." + bubbleEvent, task].concat(__slice.call(args)));
- });
- });
- this.emit('task.add', task);
- return this.addItem(task);
- };
-
- TaskGroup.prototype.createGroup = function() {
- var args, group;
- args = 1 <= arguments.length ? __slice.call(arguments, 0) : [];
- group = (function(func, args, ctor) {
- ctor.prototype = func.prototype;
- var child = new ctor, result = func.apply(child, args);
- return Object(result) === result ? result : child;
- })(TaskGroup, args, function(){});
- return group;
- };
-
- TaskGroup.prototype.addGroup = function() {
- var args, group, me;
- args = 1 <= arguments.length ? __slice.call(arguments, 0) : [];
- me = this;
- group = this.createGroup.apply(this, args).setConfig({
- concurrency: this.concurrency,
- parent: this
- });
- this.bubbleEvents.forEach(function(bubbleEvent) {
- return group.on(bubbleEvent, function() {
- var args;
- args = 1 <= arguments.length ? __slice.call(arguments, 0) : [];
- return me.emit.apply(me, ["group." + bubbleEvent, group].concat(__slice.call(args)));
- });
- });
- this.emit('group.add', group);
- return this.addItem(group);
- };
-
- TaskGroup.prototype.hasItems = function() {
- return this.remaining.length !== 0;
- };
-
- TaskGroup.prototype.isReady = function() {
- return !this.concurrency || this.running < this.concurrency;
- };
-
- TaskGroup.prototype.nextItems = function() {
- var item, items, result;
- items = [];
- while (true) {
- item = this.nextItem();
- if (item) {
- items.push(item);
- } else {
- break;
- }
- }
- result = items.length ? items : false;
- return result;
- };
-
- TaskGroup.prototype.nextItem = function() {
- var nextItem;
- if (this.hasItems()) {
- if (this.isReady()) {
- nextItem = this.remaining.shift();
- ++this.running;
- nextItem.run();
- return nextItem;
- }
- }
- return false;
- };
-
- TaskGroup.prototype.complete = function() {
- var completed, empty, pause;
- pause = this.pauseOnError && this.err;
- empty = this.hasItems() === false && this.running === 0;
- completed = pause || empty;
- if (completed) {
- if (pause) {
- this.pause();
- }
- this.emit('complete', this.err, this.results);
- this.err = null;
- this.results = [];
- }
- return completed;
- };
-
- TaskGroup.prototype.clear = function() {
- var item, _i, _len, _ref;
- _ref = this.remaining.splice(0);
- for (_i = 0, _len = _ref.length; _i < _len; _i++) {
- item = _ref[_i];
- item.destroy();
- }
- return this;
- };
-
- TaskGroup.prototype.destroy = function() {
- this.stop();
- this.removeAllListeners();
- return this;
- };
-
- TaskGroup.prototype.stop = function() {
- this.pause();
- this.clear();
- return this;
- };
-
- TaskGroup.prototype.exit = function(err) {
- if (err) {
- this.err = err;
- }
- this.stop();
- this.running = 0;
- this.complete();
- return this;
- };
-
- TaskGroup.prototype.pause = function() {
- this.paused = true;
- return this;
- };
-
- TaskGroup.prototype.run = function() {
- var args, me;
- args = 1 <= arguments.length ? __slice.call(arguments, 0) : [];
- me = this;
- this.paused = false;
- this.emit('run');
- process.nextTick(function() {
- if (!me.complete()) {
- return me.nextItems();
- }
- });
- return this;
- };
-
- return TaskGroup;
-
- })(EventEmitter);
-
- module.exports = {
- Task: Task,
- TaskGroup: TaskGroup
- };
-
-}).call(this);
View
384 out/test/taskgroup-test.js
@@ -1,384 +0,0 @@
-// Generated by CoffeeScript 1.6.3
-(function() {
- var Task, TaskGroup, delay, expect, joe, wait, _ref;
-
- expect = require('chai').expect;
-
- joe = require('joe');
-
- _ref = require('../../'), Task = _ref.Task, TaskGroup = _ref.TaskGroup;
-
- wait = function(delay, fn) {
- return setTimeout(fn, delay);
- };
-
- delay = 100;
-
- joe.describe('task', function(describe, it) {
- describe("basic", function(suite, it) {
- it('should work with async', function(done) {
- var checks, task;
- checks = 0;
- task = new Task(function(complete) {
- return wait(delay, function() {
- ++checks;
- expect(task.result, "result to be null as we haven't set it yet").to.eql(null);
- return complete(null, 10);
- });
- });
- task.on('complete', function(err, result) {
- ++checks;
- expect(task.result, "the set result to be as expected as the task has completed").to.eql([err, result]);
- expect(err, "the callback error to be null as we did not error").to.eql(null);
- return expect(result, "the callback result to be as expected").to.eql(10);
- });
- expect(task.running, "running to be false as we haven't started running yet").to.eql(false);
- expect(task.result, "result to be null as we haven't started running yet").to.eql(null);
- task.run();
- expect(task.running, 'running to be true as tasks execute asynchronously').to.eql(true);
- expect(task.result, 'result to be null as tasks execute asynchronously').to.eql(null);
- return wait(delay * 2, function() {
- ++checks;
- expect(checks, "all our special checks have run").to.eql(3);
- return done();
- });
- });
- return it('should work with sync', function(done) {
- var checks, task;
- checks = 0;
- task = new Task(function() {
- ++checks;
- expect(task.result, "result to be null as we haven't set it yet").to.eql(null);
- return 10;
- });
- task.on('complete', function(err, result) {
- ++checks;
- expect(task.result, "the set result to be as expected as the task has completed").to.eql([err, result]);
- expect(err, "the callback error to be null as we did not error").to.eql(null);
- return expect(result, "the callback result to be as expected").to.eql(10);
- });
- expect(task.running, "running to be false as we haven't started running yet").to.eql(false);
- expect(task.result, "result to be null as we haven't started running yet").to.eql(null);
- task.run();
- expect(task.running, 'running to be true as tasks execute asynchronously').to.eql(true);
- expect(task.result, 'result to be null as tasks execute asynchronously').to.eql(null);
- return wait(delay, function() {
- ++checks;
- expect(checks, "all our special checks have run").to.eql(3);
- return done();
- });
- });
- });
- describe("errors", function(suite, it) {
- it('should detect return error on synchronous task', function(done) {
- var checks, err, errMessage, task;
- checks = 0;
- errMessage = 'deliberate return error';
- err = new Error(errMessage);
- task = new Task(function() {
- ++checks;
- expect(task.result, "result to be null as we haven't set it yet").to.eql(null);
- return err;
- });
- task.on('complete', function(_err, result) {
- ++checks;
- expect(task.result, "the set result to be as expected as the task has completed").to.eql([err]);
- expect(_err, "the callback error to be set as we errord").to.eql(err);
- return expect(result, "the callback result to be null we errord").to.not.exist;
- });
- expect(task.running, "running to be false as we haven't started running yet").to.eql(false);
- expect(task.result, "result to be null as we haven't started running yet").to.eql(null);
- task.run();
- expect(task.running, 'running to be true as tasks execute asynchronously').to.eql(true);
- expect(task.result, 'result to be null as tasks execute asynchronously').to.eql(null);
- return wait(delay, function() {
- ++checks;
- expect(checks, "all our special checks have run").to.eql(3);
- return done();
- });
- });
- it('should detect sync throw error on synchronous task', function(done) {
- var checks, err, errMessage, neverReached, task;
- checks = 0;
- neverReached = false;
- errMessage = 'deliberate sync throw error';
- err = new Error(errMessage);
- task = new Task(function() {
- ++checks;
- expect(task.result, "result to be null as we haven't set it yet").to.eql(null);
- throw err;
- });
- task.on('complete', function(_err, result) {
- return neverReached = true;
- });
- task.on('error', function(_err) {
- ++checks;
- return expect(_err, "the callback error to be set as we errord").to.eql(err);
- });
- expect(task.running, "running to be false as we haven't started running yet").to.eql(false);
- expect(task.result, "result to be null as we haven't started running yet").to.eql(null);
- task.run();
- expect(task.running, 'running to be true as tasks execute asynchronously').to.eql(true);
- expect(task.result, 'result to be null as tasks execute asynchronously').to.eql(null);
- return wait(delay, function() {
- ++checks;
- expect(checks, "all our special checks have run").to.eql(3);
- expect(neverReached, "never reached to be false").to.eql(false);
- return done();
- });
- });
- return it('should detect async throw error on asynchronous task', function(done) {
- var checks, err, errMessage, neverReached, task;
- if (process.versions.node.substr(0, 3) === '0.8') {
- console.log('skip this test on node 0.8 because domains behave differently');
- return done();
- }
- checks = 0;
- neverReached = false;
- errMessage = 'deliberate async throw error';
- err = new Error(errMessage);
- task = new Task(function(done) {
- return wait(delay, function() {
- ++checks;
- expect(task.result, "result to be null as we haven't set it yet").to.eql(null);
- throw err;
- });
- });
- task.on('complete', function(_err, result) {
- return neverReached = true;
- });
- task.on('error', function(_err) {
- ++checks;
- return expect(_err, "the callback error to be set as we errord").to.eql(err);
- });
- expect(task.running, "running to be false as we haven't started running yet").to.eql(false);
- expect(task.result, "result to be null as we haven't started running yet").to.eql(null);
- task.run();
- expect(task.running, 'running to be true as tasks execute asynchronously').to.eql(true);
- expect(task.result, 'result to be null as tasks execute asynchronously').to.eql(null);
- return wait(delay * 2, function() {
- ++checks;
- expect(checks, "all our special checks have run").to.eql(3);
- expect(neverReached, "never reached to be false").to.eql(false);
- return done();
- });
- });
- });
- return describe("arguments", function(suite, it) {
- it('should work with arguments in sync', function(done) {
- var checks, task;
- checks = 0;
- task = new Task(function(a, b) {
- ++checks;
- expect(task.result).to.eql(null);
- return a * b;
- });
- task.setConfig({
- args: [2, 5]
- });
- task.on('complete', function(err, result) {
- ++checks;
- expect(task.result).to.eql([err, result]);
- expect(err).to.eql(null);
- return expect(result).to.eql(10);
- });
- wait(1000, function() {
- ++checks;
- expect(checks).to.eql(3);
- return done();
- });
- return task.run();
- });
- return it('should work with arguments in async', function(done) {
- var checks, task;
- checks = 0;
- task = new Task(function(a, b, complete) {
- return wait(500, function() {
- ++checks;
- expect(task.result).to.eql(null);
- return complete(null, a * b);
- });
- });
- task.setConfig({
- args: [2, 5]
- });
- task.on('complete', function(err, result) {
- ++checks;
- expect(task.result).to.eql([err, result]);
- expect(err).to.eql(null);
- return expect(result).to.eql(10);
- });
- wait(1000, function() {
- ++checks;
- expect(checks).to.eql(3);
- return done();
- });
- return task.run();
- });
- });
- });
-
- joe.describe('taskgroup', function(describe, it) {
- describe("basic", function(suite, it) {
- it('should work when running in serial', function(done) {
- var tasks;
- tasks = new TaskGroup().setConfig({
- concurrency: 1
- }).on('complete', function(err, results) {
- expect(err).to.eql(null);
- expect(results).to.eql([[null, 10], [null, 5]]);
- expect(tasks.remaining.length).to.eql(0);
- expect(tasks.running).to.eql(0);
- expect(tasks.concurrency).to.eql(1);
- return done();
- });
- tasks.addTask(function(complete) {
- expect(tasks.remaining.length).to.eql(1);
- expect(tasks.running).to.eql(1);
- return wait(500, function() {
- expect(tasks.remaining.length).to.eql(1);
- expect(tasks.running).to.eql(1);
- return complete(null, 10);
- });
- });
- tasks.addTask(function() {
- expect(tasks.remaining.length).to.eql(0);
- expect(tasks.running).to.eql(1);
- return 5;
- });
- return tasks.run();
- });
- return it('should work when running in parallel', function(done) {
- var tasks;
- tasks = new TaskGroup().setConfig({
- concurrency: 0
- }).on('complete', function(err, results) {
- expect(err).to.eql(null);
- expect(results).to.eql([[null, 5], [null, 10]]);
- expect(tasks.remaining.length).to.eql(0);
- expect(tasks.running).to.eql(0);
- expect(tasks.concurrency).to.eql(0);
- return done();
- });
- tasks.addTask(function(complete) {
- expect(tasks.remaining.length).to.eql(0);
- expect(tasks.running).to.eql(2);
- return wait(500, function() {
- expect(tasks.remaining.length).to.eql(0);
- expect(tasks.running).to.eql(1);
- return complete(null, 10);
- });
- });
- tasks.addTask(function() {
- expect(tasks.remaining.length).to.eql(0);
- expect(tasks.running).to.eql(2);
- return 5;
- });
- return tasks.run();
- });
- });
- return describe("errors", function(suite, it) {
- it('should handle error correctly in parallel', function(done) {
- var tasks;
- tasks = new TaskGroup().setConfig({
- concurrency: 0
- }).on('complete', function(err, results) {
- expect(err.message).to.eql('deliberate error');
- expect(results.length).to.eql(1);
- expect(tasks.remaining.length).to.eql(0);
- expect(tasks.running).to.eql(1);
- expect(tasks.concurrency).to.eql(0);
- return done();
- });
- tasks.addTask(function(complete) {
- expect(tasks.remaining.length).to.eql(0);
- expect(tasks.running).to.eql(2);
- wait(500, function() {
- var err;
- err = new Error('deliberate error');
- return complete(err);
- });
- return null;
- });
- tasks.addTask(function() {
- var err;
- expect(tasks.remaining.length).to.eql(0);
- expect(tasks.running).to.eql(2);
- err = new Error('deliberate error');
- return err;
- });
- return tasks.run();
- });
- return it('should handle error correctly in serial', function(done) {
- var tasks;
- tasks = new TaskGroup().setConfig({
- concurrency: 1
- }).on('complete', function(err, results) {
- expect(err.message).to.eql('deliberate error');
- expect(results.length).to.eql(1);
- expect(tasks.remaining.length).to.eql(1);
- expect(tasks.running).to.eql(0);
- expect(tasks.concurrency).to.eql(1);
- return done();
- });
- tasks.addTask(function(complete) {
- var err;
- expect(tasks.remaining.length).to.eql(1);
- expect(tasks.running).to.eql(1);
- err = new Error('deliberate error');
- return complete(err);
- });
- tasks.addTask(function() {
- throw 'unexpected';
- });
- return tasks.run();
- });
- });
- });
-
- joe.describe('inline', function(describe, it) {
- return it('should work', function(done) {
- var checks, tasks;
- checks = [];
- tasks = new TaskGroup('my tests', function(addGroup, addTask) {
- expect(this.name).to.eql('my tests');
- addTask('my task', function(complete) {
- checks.push('my task 1');
- expect(this.name).to.eql('my task');
- expect(tasks.remaining.length).to.eql(1);
- expect(tasks.running).to.eql(1);
- return wait(500, function() {
- checks.push('my task 2');
- expect(tasks.remaining.length).to.eql(1);
- expect(tasks.running).to.eql(1);
- return complete();
- });
- });
- return addGroup('my group', function(addGroup, addTask) {
- checks.push('my group');
- expect(this.name).to.eql('my group');
- expect(tasks.remaining.length, 'my group remaining').to.eql(0);
- expect(tasks.running).to.eql(1);
- return addTask('my second task', function() {
- checks.push('my second task');
- expect(this.name).to.eql('my second task');
- expect(tasks.remaining.length, 'my second task remaining').to.eql(0);
- return expect(tasks.running).to.eql(1);
- });
- });
- });
- return tasks.on('complete', function(err) {
- if (err) {
- console.log(err);
- }
- expect(err).to.eql(null);
- if (checks.length !== 4) {
- console.log(checks);
- }
- expect(checks.length, 'checks').to.eql(4);
- return done();
- });
- });
- });
-
-}).call(this);
View
7 package.json
@@ -1,7 +1,7 @@
{
"title": "TaskGroup",
"name": "taskgroup",
- "version": "3.2.4",
+ "version": "3.3.0",
"description": "Group together synchronous and asynchronous tasks and execute them with support for concurrency, naming, and nesting.",
"homepage": "https://github.com/bevry/taskgroup",
"license": {
@@ -23,12 +23,13 @@
"batch",
"concurrency"
],
- "author": "Benjamin Lupton <b@lupton.cc>",
+ "author": "2013+ Bevry Pty Ltd <us@bevry.me> (http://bevry.me), 2011-2012 Benjamin Lupton <b@lupton.cc> (http://balupton.com)",
"maintainers": [
"Benjamin Lupton <b@lupton.cc> (https://github.com/balupton)"
],
"contributors": [
- "Benjamin Lupton <b@lupton.cc> (https://github.com/balupton)"
+ "Benjamin Lupton <b@lupton.cc> (https://github.com/balupton)",
+ "sfrdmn (https://github.com/sfrdmn)"
],
"bugs": {
"url": "https://github.com/bevry/taskgroup/issues"
View
213 src/lib/taskgroup.coffee
@@ -1,7 +1,11 @@
# Import
ambi = require('ambi')
-events = if window? then require('events-browser') else require('events')
-domain = if window? then require('domain-browser') else require('domain')
+events = require('events')
+domain =
+ try
+ require('domain')
+ catch err
+ null
{EventEmitter} = events
# Task
@@ -14,13 +18,16 @@ class Task extends EventEmitter
result: null
running: false
completed: false
- parent: null
taskDomain: null
# Config
- name: null
- method: null
- args: null
+ config: null
+ ###
+ name: null
+ method: null
+ args: null
+ parent: null
+ ###
# Create a new task
# - new Task(name, method)
@@ -28,22 +35,25 @@ class Task extends EventEmitter
constructor: (args...) ->
# Prepare
super
+ @config ?= {}
+ @config.run ?= false
# Prepare configuration
- name = method = null
+ opts = {}
# Extract the configuration from the arguments
- if args.length
- # If we have both arguments then set name and method
- if args.length is 2
- [name, method] = args
-
- # If we just have one argument then just set the method
- else if args.length is 1
- [method] = args
+ for arg in args
+ switch typeof arg
+ when 'string'
+ opts.name = arg
+ when 'function'
+ opts.method = arg
+ when 'object'
+ for own key,value of arg
+ opts[key] = value
# Apply configuration
- @setConfig({name, method})
+ @setConfig(opts)
# Chain
@
@@ -52,11 +62,18 @@ class Task extends EventEmitter
setConfig: (opts={}) ->
# Apply the configuration directly to our instance
for own key,value of opts
- @[key] = value
+ switch key
+ when 'next'
+ @once('complete', value.bind(@)) if value
+ else
+ @config[key] = value
# Chain
@
+ # Get Config
+ getConfig: -> @config
+
# Reset
reset: ->
# Reset our flags
@@ -121,20 +138,28 @@ class Task extends EventEmitter
# Fire
fire: ->
+ # Prepare
+ me = @
+
# Add our completion callback to our specified arguments to send over to the method
- args = (@args or []).concat([@completionCallback.bind(@)])
+ args = (@config.args or []).concat([@completionCallback.bind(@)])
# Prepare the task domain if it doesn't already exist
- unless @taskDomain?
+ if @taskDomain? is false and domain?.create?
@taskDomain = domain.create()
@taskDomain.on('error', @uncaughtExceptionCallback.bind(@))
# Listen for uncaught errors
- @taskDomain.run =>
+ fire = ->
try
- ambi(@method.bind(@), args...)
+ ambi(me.config.method.bind(me), args...)
catch err
- @uncaughtExceptionCallback(err)
+ me.uncaughtExceptionCallback(err)
+
+ if @taskDomain?
+ @taskDomain.run(fire)
+ else
+ fire()
# Chain
@
@@ -174,39 +199,47 @@ class TaskGroup extends EventEmitter
remaining: null
err: null
results: null
- parent: null
paused: true
bubbleEvents: null
# Config
- name: null
- method: null
- concurrency: 1 # use 0 for unlimited
- pauseOnError: true # needs testing
+ config: null
+ ###
+ name: null
+ method: null
+ concurrency: 1 # use 0 for unlimited
+ pauseOnError: true
+ parent: null
+ ###
constructor: (args...) ->
# Init
me = @
super
+ @config ?= {}
+ @config.concurrency ?= 1
+ @config.pauseOnError ?= true
+ @config.run ?= false
@results ?= []
@remaining ?= []
@bubbleEvents ?= ['complete', 'run', 'error']
# Prepare configuration
- name = method = null
+ opts = {}
# Extract the configuration from the arguments
- if args.length
- # If we have both arguments then set name and method
- if args.length is 2
- [name, method] = args
-
- # If we just have one argument then just set the method
- else if args.length is 1
- [method] = args
+ for arg in args
+ switch typeof arg
+ when 'string'
+ opts.name = arg
+ when 'function'
+ opts.method = arg
+ when 'object'
+ for own key,value of arg
+ opts[key] = value
# Apply configuration
- @setConfig({name, method})
+ @setConfig(opts)
# Give setConfig enough chance to fire
process.nextTick(@fire.bind(@))
@@ -223,29 +256,46 @@ class TaskGroup extends EventEmitter
@
setConfig: (opts={}) ->
- # Configure
+ # Apply the configuration directly to our instance
for own key,value of opts
- @[key] = value
+ switch key
+ when 'next'
+ @once('complete', value.bind(@)) if value
+ when 'task', 'tasks'
+ @addTasks(value) if value
+ when 'group', 'groups'
+ @addGroups(value) if value
+ when 'item', 'items'
+ @addItems(value) if value
+ else
+ @config[key] = value
# Chain
@
+ getConfig: -> @config
+
fire: ->
# Auto run if we are going the inline style and have no parent
- if @method
+ if @config.method
# Add the function as our first unamed task with the extra arguments
- args = [@addGroup, @addTask]
- @addTask(@method.bind(@)).setConfig({args, includeInResults:false})
+ @addTask(@config.method.bind(@), {
+ args: [@addGroup.bind(@), @addTask.bind(@)]
+ includeInResults:false
+ })
# Proceed to run if we are the topmost group
- @run() if !@parent
+ @run() if !@config.parent
+
+ # Auto run if we are ocnfigured to
+ @run() if @config.run is true
# Chain
@
itemCompletionCallback: (item, args...) ->
# Add the result
- @results.push(args) if item.includeInResults isnt false
+ @results.push(args) if item.config.includeInResults isnt false
# Update error if it exists
@err = args[0] if args[0]
@@ -280,6 +330,28 @@ class TaskGroup extends EventEmitter
# Prepare
me = @
+ # Link our item to ourself
+ item.setConfig({parent: @})
+
+ # Bubble task events
+ if item instanceof Task
+ @bubbleEvents.forEach (bubbleEvent) ->
+ item.on bubbleEvent, (args...) ->
+ me.emit("task.#{bubbleEvent}", item, args...)
+
+ # Notify our intention
+ @emit('task.add', item)
+
+ # Bubble group events
+ if item instanceof TaskGroup
+ # Bubble item events
+ @bubbleEvents.forEach (bubbleEvent) ->
+ item.on bubbleEvent, (args...) ->
+ me.emit("group.#{bubbleEvent}", item, args...)
+
+ # Notify our intention
+ @emit('group.add', item)
+
# Bubble item events
@bubbleEvents.forEach (bubbleEvent) ->
item.on bubbleEvent, (args...) ->
@@ -297,49 +369,32 @@ class TaskGroup extends EventEmitter
# Return the item
return item
- createTask: (args...) ->
- task = new Task(args...)
- return task
+ addItems: (items, args...) ->
+ items = [items] unless Array.isArray(items)
+ return (@addItem(item, args...) for item in items)
- addTask: (args...) =>
- # Prepare
- me = @
- # Create the task with our arguments
- task = @createTask(args...).setConfig({parent:@})
+ createTask: (args...) ->
+ return new Task(args...)
- # Bubble item events
- @bubbleEvents.forEach (bubbleEvent) ->
- task.on bubbleEvent, (args...) ->
- me.emit("task.#{bubbleEvent}", task, args...)
+ addTask: (args...) ->
+ return @addItem @createTask args...
- # Notify our intention
- @emit('task.add', task)
+ addTasks: (items, args...) ->
+ items = [items] unless Array.isArray(items)
+ return (@addTask(item, args...) for item in items)
- # Return the item
- return @addItem(task)
createGroup: (args...) ->
- group = new TaskGroup(args...)
- return group
+ return new TaskGroup(args...)
- addGroup: (args...) =>
- # Prepare
- me = @
-
- # Create the group with our arguments
- group = @createGroup(args...).setConfig({concurrency:@concurrency,parent:@})
-
- # Bubble item events
- @bubbleEvents.forEach (bubbleEvent) ->
- group.on bubbleEvent, (args...) ->
- me.emit("group.#{bubbleEvent}", group, args...)
+ addGroup: (args...) ->
+ return @addItem @createGroup args...
- # Notify our intention
- @emit('group.add', group)
+ addGroups: (items, args...) ->
+ items = [items] unless Array.isArray(items)
+ return (@addGroup(item, args...) for item in items)
- # Return the item
- return @addItem(group)
hasItems: ->
# Do we have any items left to run
@@ -347,7 +402,7 @@ class TaskGroup extends EventEmitter
isReady: ->
# Do we have available slots to run
- return !@concurrency or @running < @concurrency
+ return !@config.concurrency or @running < @config.concurrency
nextItems: ->
items = []
@@ -383,7 +438,7 @@ class TaskGroup extends EventEmitter
complete: ->
# Determine completion
- pause = @pauseOnError and @err
+ pause = @config.pauseOnError and @err
empty = @hasItems() is false and @running is 0
completed = pause or empty
View
44 src/test/taskgroup-test.coffee
@@ -282,7 +282,7 @@ joe.describe 'taskgroup', (describe,it) ->
expect(results).to.eql([[null,10], [null,5]])
expect(tasks.remaining.length).to.eql(0)
expect(tasks.running).to.eql(0)
- expect(tasks.concurrency).to.eql(1)
+ expect(tasks.config.concurrency).to.eql(1)
done()
tasks.addTask (complete) ->
@@ -300,14 +300,14 @@ joe.describe 'taskgroup', (describe,it) ->
tasks.run()
- # Parallel
+ # Parallel with new API
it 'should work when running in parallel', (done) ->
tasks = new TaskGroup().setConfig({concurrency:0}).on 'complete', (err,results) ->
expect(err).to.eql(null)
expect(results).to.eql([[null,5],[null,10]])
expect(tasks.remaining.length).to.eql(0)
expect(tasks.running).to.eql(0)
- expect(tasks.concurrency).to.eql(0)
+ expect(tasks.config.concurrency).to.eql(0)
done()
tasks.addTask (complete) ->
@@ -325,6 +325,32 @@ joe.describe 'taskgroup', (describe,it) ->
tasks.run()
+ # Parallel
+ it 'should work when running in parallel with new API', (done) ->
+ tasks = new TaskGroup(
+ concurrency: 0
+ next: (err,results) ->
+ expect(err).to.eql(null)
+ expect(results).to.eql([[null,5],[null,10]])
+ expect(tasks.remaining.length).to.eql(0)
+ expect(tasks.running).to.eql(0)
+ expect(tasks.config.concurrency).to.eql(0)
+ done()
+ tasks: [
+ (complete) ->
+ expect(tasks.remaining.length).to.eql(0)
+ expect(tasks.running).to.eql(2)
+ wait 500, ->
+ expect(tasks.remaining.length).to.eql(0)
+ expect(tasks.running).to.eql(1)
+ complete(null,10)
+ ->
+ expect(tasks.remaining.length).to.eql(0)
+ expect(tasks.running).to.eql(2)
+ return 5
+ ]
+ ).run()
+
# Basic
describe "errors", (suite,it) ->
# Parallel
@@ -334,7 +360,7 @@ joe.describe 'taskgroup', (describe,it) ->
expect(results.length).to.eql(1)
expect(tasks.remaining.length).to.eql(0)
expect(tasks.running).to.eql(1)
- expect(tasks.concurrency).to.eql(0)
+ expect(tasks.config.concurrency).to.eql(0)
done()
# Error via completion callback
@@ -363,7 +389,7 @@ joe.describe 'taskgroup', (describe,it) ->
expect(results.length).to.eql(1)
expect(tasks.remaining.length).to.eql(1)
expect(tasks.running).to.eql(0)
- expect(tasks.concurrency).to.eql(1)
+ expect(tasks.config.concurrency).to.eql(1)
done()
tasks.addTask (complete) ->
@@ -386,11 +412,11 @@ joe.describe 'inline', (describe,it) ->
checks = []
tasks = new TaskGroup 'my tests', (addGroup,addTask) ->
- expect(@name).to.eql('my tests')
+ expect(@config.name).to.eql('my tests')
addTask 'my task', (complete) ->
checks.push('my task 1')
- expect(@name).to.eql('my task')
+ expect(@config.name).to.eql('my task')
expect(tasks.remaining.length).to.eql(1)
expect(tasks.running).to.eql(1)
wait 500, ->
@@ -401,13 +427,13 @@ joe.describe 'inline', (describe,it) ->
addGroup 'my group', (addGroup,addTask) ->
checks.push('my group')
- expect(@name).to.eql('my group')
+ expect(@config.name).to.eql('my group')
expect(tasks.remaining.length, 'my group remaining').to.eql(0)
expect(tasks.running).to.eql(1)
addTask 'my second task', ->
checks.push('my second task')
- expect(@name).to.eql('my second task')
+ expect(@config.name).to.eql('my second task')
expect(tasks.remaining.length, 'my second task remaining').to.eql(0)
expect(tasks.running).to.eql(1)
Please sign in to comment.
Something went wrong with that request. Please try again.