taskgroup
Advanced tools
Comparing version 3.3.2 to 3.3.3
@@ -1,3 +0,6 @@ | ||
## History | ||
# History | ||
- v3.3.3 November 27, 2013 | ||
- Fixed possible "(node) warning: Recursive process.nextTick detected. This will break in the next version of node. Please use setImmediate for recursive deferral." error under certain circumstances | ||
- v3.3.2 November 19, 2013 | ||
@@ -4,0 +7,0 @@ - Don't add or create empty tasks and groups |
@@ -181,3 +181,3 @@ // Generated by CoffeeScript 1.6.3 | ||
this.emit('run'); | ||
process.nextTick(this.fire.bind(this)); | ||
setImmediate(this.fire.bind(this)); | ||
} | ||
@@ -267,3 +267,3 @@ return this; | ||
this.setConfig(opts); | ||
process.nextTick(this.fire.bind(this)); | ||
setImmediate(this.fire.bind(this)); | ||
this.on('item.complete', this.itemCompletionCallback.bind(this)); | ||
@@ -270,0 +270,0 @@ this.on('item.error', this.itemUncaughtExceptionCallback.bind(this)); |
{ | ||
"title": "TaskGroup", | ||
"name": "taskgroup", | ||
"version": "3.3.2", | ||
"version": "3.3.3", | ||
"description": "Group together synchronous and asynchronous tasks and execute them with support for concurrency, naming, and nesting.", | ||
@@ -6,0 +6,0 @@ "homepage": "https://github.com/bevry/taskgroup", |
365
README.md
@@ -1,2 +0,1 @@ | ||
<!-- TITLE/ --> | ||
@@ -11,3 +10,2 @@ | ||
[![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](http://badge.fury.io/js/taskgroup.png)](https://npmjs.org/package/taskgroup "View this project on NPM") | ||
@@ -42,71 +40,316 @@ [![Gittip donate button](http://img.shields.io/gittip/bevry.png)](https://www.gittip.com/bevry/ "Donate weekly to this project using Gittip") | ||
## Contents | ||
1. [Usage](#usage) | ||
2. [API](#api) | ||
3. [Comparisons to other flow libraries](#comparisons) | ||
4. [Libraries built on top of TaskGroup](#libraries) | ||
## Usage | ||
### Example | ||
TaskGroup provides two classes, `Task` and `TaskGroup` | ||
### Tasks | ||
Tasks are used to wrap a function (synchronous or asynchronous, it doesn't matter) inside a task execution flow. | ||
This is useful for using a consistent interface for executing tasks and doing something on their completion or failure, as well as catching uncaught errors and handling them safely. | ||
We can define a synchronous task like so: | ||
``` javascript | ||
// Import | ||
var TaskGroup = require('taskgroup').TaskGroup; | ||
var Task = require('taskgroup').Task | ||
// Create our new group | ||
var group = new TaskGroup(); | ||
// Create a task for our synchronous function | ||
var task = new Task(function(){ | ||
// Do something ... | ||
return "a synchronous result"; | ||
// Define what should happen once the group has completed | ||
group.once('complete', function(err, results){ | ||
// Log the error that has occured | ||
console.log(err); | ||
// => null | ||
// You can also return an error | ||
// return new Error("something went wrong") | ||
}); | ||
// Log the results that our group received from the executing items | ||
console.log(JSON.stringify(results)); | ||
/* => | ||
[ | ||
[null, 'first', 'task'], | ||
[null, 'second task'], | ||
[null, [ | ||
[null, 'sub second task'], | ||
[null, 'sub first', 'task'] | ||
]] | ||
] | ||
*/ | ||
// Add our completion callback for once the task has completed | ||
task.once('complete', function(err, result){ | ||
// Do something now that the task has completed ... | ||
console.log([err, result]); | ||
/* [null, "a sychronous result"] */ | ||
}); | ||
// Add an asynchronous task that gives the result to the completion callback | ||
group.addTask(function(complete){ | ||
// Execute the task | ||
task.run(); | ||
``` | ||
And an asynchronous task like so: | ||
``` javascript | ||
var Task = require('taskgroup').Task | ||
// Create a task for our synchronous function | ||
var task = new Task(function(complete){ | ||
// Do something asynchronous | ||
setTimeout(function(){ | ||
complete(null, 'first', 'task'); | ||
},500); | ||
// the error is the first callback argument, and the results the following arguments | ||
return complete(null, "an asychronous result"); | ||
// So to provide an error instead, you would just pass over the first callback argument | ||
// return complete("something went wrong") | ||
}, 5000); // execute the timeout after 5 seconds | ||
}); | ||
// Add a synchronous task that returns the result | ||
// Errors should be returned, though if an error is thrown we will catch it | ||
group.addTask(function(){ | ||
return 'second task'; | ||
// Add our completion callback for once the task has completed | ||
task.once('complete', function(err, result){ | ||
// Do something now that the task has completed ... | ||
console.log([err, result]); | ||
/* [null, "an asychronous result"] */ | ||
}); | ||
// Add a sub-group to our exiting group | ||
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) | ||
this.setConfig({concurrency:0}); | ||
// Execute the task | ||
task.run(); | ||
``` | ||
// Add an asynchronous task that gives its result to the completion callback | ||
### TaskGroup | ||
Often at times, we want to execute multipe things and wait for the completion. TaskGroup makes this easy with the other class, `TaskGroup`. | ||
We simply create a `TaskGroup` and add out Tasks to it! | ||
``` javascript | ||
var TaskGroup = require('taskgroup').TaskGroup | ||
// Create our serial task group | ||
var tasks = new TaskGroup(); | ||
// Add an asynchronous task to it | ||
tasks.addTask(function(complete){ | ||
setTimeout(function(){ | ||
return complete(null, "a result"); | ||
}, 5000); // execute the timeout after 5 seconds | ||
}); | ||
// Add a synchronous task to it | ||
tasks.addTask(function(){ | ||
return "a synchronous result"; | ||
}); | ||
// Add our completion callback for once the tasks have completed | ||
tasks.once('complete', function(err, results){ | ||
console.log([err, result]); | ||
/* [null, [ | ||
[null, "an asychronous result"], | ||
[null, "a sychronous result"] | ||
]] */ | ||
}); | ||
// Execute the task group | ||
tasks.run(); | ||
``` | ||
Now by default, the TaskGroup will execute serially. This means that each task will execute one by one, waiting for the previous task to complete before moving on to the next task. This can also be considered having a concurrency of `1`. This is called _serial_ execution. | ||
If we wanted to execute say two tasks at a time we could want a concurrency of `2`, or three tasks at a time, a concurrency of `3` would be set, or unlimited tasks at a time, a concurrency of `0` would be set. | ||
We can customise the concurrency of the task group by passing it over as a configuration option, either via the TaskGroup constructor or via the `setConfig` method. Let's see what this would look like if we were do a concurrency of `0`. This is called _parallel_ execution. | ||
``` javascript | ||
var TaskGroup = require('taskgroup').TaskGroup | ||
// Create our parellel task group | ||
var tasks = new TaskGroup({concurrency:0}); | ||
// Add an asynchronous task to it | ||
tasks.addTask(function(complete){ | ||
setTimeout(function(){ | ||
return complete(null, "a result"); | ||
}, 5000); // execute the timeout after 5 seconds | ||
}); | ||
// Add a synchronous task to it | ||
tasks.addTask(function(){ | ||
return "a synchronous result"; | ||
}); | ||
// Add our completion callback for once the tasks have completed | ||
tasks.once('complete', function(err, results){ | ||
console.log([err, result]); | ||
/* [null, [ | ||
[null, "a sychronous result"], | ||
[null, "an asychronous result"] | ||
]] */ | ||
}); | ||
// Execute the task group | ||
tasks.run(); | ||
``` | ||
Notice how the groups results are now in a different order. This occured because with parallel execution, we didn't have to wait for the asynchronous fucntion to complete it's 5 second delay before executing and completing the second function (the synchronous one). | ||
You can mix and match as many functions as you want with TaskGroups. | ||
### Nested TaskGroups | ||
You can also nest TaskGroups inside TaskGroups. | ||
A common use case for this is when you would like a portion of your tasks to execute in parallel, and portion of your tasks to execute in serial. | ||
Such a use case would look like so: | ||
``` javascript | ||
var TaskGroup = require('taskgroup').TaskGroup | ||
// Create our serial task group | ||
var tasks = new TaskGroup(); | ||
// Add the first serial task | ||
tasks.addTask(function(){ | ||
return "first serial task"; | ||
}); | ||
// Add a nested group of tasks that you would like executed in parallel | ||
tasks.addGroup(function(addGroup, addTask){ | ||
// Set this nested group to execute in parallel | ||
this.setConfig({concurrency: 0}); | ||
// Add an asynchronous task to the nested group | ||
addTask(function(complete){ | ||
setTimeout(function(){ | ||
complete(null, 'sub first', 'task'); | ||
},500); | ||
return complete(null, "a result"); | ||
}, 5000); // execute the timeout after 5 seconds | ||
}); | ||
// Add a synchronous task that returns its result | ||
// Add a synchronous task to the nested group | ||
addTask(function(){ | ||
return 'sub second task'; | ||
return "a synchronous result"; | ||
}); | ||
}); | ||
// Execute our group | ||
group.run(); | ||
// Add the second serial task | ||
tasks.addTask(function(){ | ||
return "second serial task"; | ||
}); | ||
// Add our completion callback for once the tasks have completed | ||
tasks.once('complete', function(err, results){ | ||
console.log([err, result]); | ||
/* [null, [ | ||
[null, "first serial task"], | ||
[null, [ | ||
[null, "a sychronous result"], | ||
[null, "an asychronous result"] | ||
]], | ||
[null, "second serial task"] | ||
]] */ | ||
}); | ||
// Execute the task group | ||
tasks.run(); | ||
``` | ||
### Handling Errors | ||
Safely handling errors is an important thing to do. TaskGroup makes this easy by safely catching any errors that your task my throw, isolating the destruction to the task alone, and providing to the task or taskgroup's completion callback. | ||
When an error is detected, the remaining tasks in a TaskGroup will be cleared, and the TaskGroup's completion callback with the error will be fired. | ||
``` javascript | ||
var TaskGroup = require('taskgroup').TaskGroup | ||
// Create our serial task group | ||
var tasks = new TaskGroup(); | ||
// Add a synchronous task to the TaskGroup | ||
tasks.addTask(function(complete){ | ||
setTimeout(function(){ | ||
return complete(new Error("the first task failed")) | ||
}, 5000); // execute the timeout after 5 second | ||
}); | ||
// Add a synchronous task to the TaskGroup | ||
tasks.addTask(function(){ | ||
return "the second task"; | ||
}); | ||
// Add our completion callback for once the tasks have completed | ||
tasks.once('complete', function(err, results){ | ||
console.log([err, result]); | ||
/* [Error("the first task failed"), [ | ||
[Error("the first task failed")] | ||
]] */ | ||
}); | ||
// Execute the task group | ||
tasks.run(); | ||
``` | ||
Which comes in very handling with dealing with asynchronous parallel code: | ||
``` javascript | ||
var TaskGroup = require('taskgroup').TaskGroup | ||
// Create our parallel task group | ||
var tasks = new TaskGroup({concurrency: 0}); | ||
// Add an asynchronous task to the TaskGroup | ||
tasks.addTask(function(){ | ||
setTimeout(function(){ | ||
return complete("the first task failed"); | ||
}, 5000); // execute the timeout after 5 seconds | ||
}); | ||
// Add an asynchronous task to the TaskGroup | ||
tasks.addTask(function(){ | ||
setTimeout(function(){ | ||
return complete("the second task failed"); | ||
}, 1000); // execute the timeout after 1 seconds | ||
}); | ||
// Add our completion callback for once the tasks have completed | ||
tasks.once('complete', function(err, results){ | ||
console.log([err, result]); | ||
/* [Error("the second task failed"), [ | ||
[Error("the second task failed")] | ||
]] */ | ||
}); | ||
// Execute the task group | ||
tasks.run(); | ||
``` | ||
Now even though the first task's completion callback still fires, it is sucessfully ignored, as the TaskGroup has exited. | ||
### Graduation | ||
Now you know all the essentials to getting started with coding the most amazing (a)synchronous parallel/serial code in your life. Enjoy! | ||
## API | ||
### Task API | ||
``` javascript | ||
new (require('taskgroup')).Task() | ||
``` | ||
- Available methods: | ||
- `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 | ||
- `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 | ||
### TaskGroup API | ||
@@ -154,26 +397,7 @@ | ||
### Task API | ||
``` javascript | ||
new (require('taskgroup')).Task() | ||
``` | ||
## Comparisons | ||
- Available methods: | ||
- `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 | ||
- `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 | ||
### [Async.js](https://github.com/caolan/async) | ||
## 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. | ||
@@ -245,2 +469,11 @@ | ||
## Libraries | ||
These are libaries and extensions that are built ontop of TaskGroup's robust API. | ||
- [Joe Test Runner](https://github.com/bevry/joe) — Mocha falls down when you have to create your tests dynamically, because Tests in Joe are Tasks, and Suites are TaskGroups, Joe will always know which tests are for which suite. Works tremendously well, with a modular architecture. Also works in the browser! | ||
- [Event Emitter Grouped](https://github.com/bevry/event-emitter-grouped) — Execute event listeners as TaskGroups, adding support for asynchronous listeners, parallel execution, and completion callbacks. Great for plugin infrastructures. | ||
<!-- HISTORY/ --> | ||
@@ -247,0 +480,0 @@ |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
62323
534