sproutcore/sproutcore — pull request #874 by krotscheck (2013-01-02 20:06:23+00:00)
Title: New Framework Candidate: SC.Task

The SproutCore Task Framework was built to simplify and encapsulate common business logic into easily testable, reusable components. Rather than build a large amount of custom logic in each controller, some of which may be reusable, you instead construct a series of small, atomic, configurable tasks.

Because of the generic set of events that are fired by the Task Framework, you can chain multiple different tasks to run either sequentially or in parallel, and again chain those Task Groups to run sequentially or in parallel as well. Tasks may even be taught how to rewind themselves, so that your application can easily and quickly recover from unexpected error conditions.

NOTE: The Task framework was designed to have a light memory footprint - task instances are expected to have a very short lifespan. As such, SproutCore best practices which construct memory pointers - such as Bindings - are not suited for use inside of tasks as they could cause memory leaks.

Let us take a login flow as an example. When a user logs in, the following steps have to occur:

* A user's record must be loaded.
* A user's preferences must be loaded.
* A user's last logged in status must be updated.

Using the task framework, this might look as follows:

```javascript
var myTask = SC.Task.SequentialTaskGroup.create({
tasks: ['loadUserRecord', 'loadUserContent'],

loadUserRecord : SC.Task.plugin('MyApp.LoadUserTask', { userId: 'foo' }),

loadUserContent : SC.ParallelTaskGroup.create({
tasks: ['loadUserPreferences', 'updateUserLoginDate'],
loadUserPreferences : SC.Task.plugin('MyApp.LoadUserPrefencesTask', { userId: 'foo' }),
updateUserLoginDate : SC.Task.plugin('MyApp.UpdateUserLoginTask', { userId: 'foo' }),
})
});

SC.TaskEvent.add(myTask, 'complete', myObj, '_onTaskComplete');
SC.TaskEvent.add(myTask, 'error', myObj, '_onTaskError');

myTask.start();
```