mjt.Task state transitions

The user calls the constructor for a particular task type to create a new instance:

var task = MyTask(...);

The new task object begins in init state.

The task will not reach wait state until all prerequisites succeed. At that point the task begins its work. Generally this involves a network request.

A ready state indicates that the task produced a useful result.

task.state == 'ready'

When a task becomes ready, the task framework notifies all pending onready() callbacks, e.g. ready_handler(result) The result of the task can also be found in task.result.

task.state == 'init'
task.state == 'wait'
or

Tasks are not started immediately after construction. After construction the user can attach handlers using task.onready(ready_handler) or task.onerror(error_handler), set up prerequisites using task.require(subtask), or set up an error timeout using task.set_timeout(milliseconds).

Finally the user calls task.enqueue() to start the task. The enqueue() is also propagated to any prerequisite tasks.

When the asynchronous task is complete, it is handled as either ready or error.

An error can be due to network timeouts, HTTP-level errors, failed prerequisite tasks, or application-specific errors.

task.state == 'error'

When a task reaches an error, the task framework notifies all pending onerror(code, message, details) callbacks.


Notes

Task states proceed strictly from left to right.

task.enqueue() may be called any number of times: if the task is not in the init state, enqueue() will be ignored.

Before calling task.enqueue(), you can set up prerequisites using task.require(subtask). The task will not enter wait state until all subtasks are ready. If any subtask goes into error state, the task goes into error immediately, without waiting for other subtasks to complete.

If task.onready(ready_handler) is called when the task is already ready, ready_handler(task.result) is called immediately. Similarly for task.onerror(error_handler) if the task is already in the error state.

Developing a new mjt.Task

To develop a new mjt.Task subclass, you need a few more hooks.

A new task type called MyTask can be created with:

  //
  // declare the new task type
  // 
  var MyTimeout = mjt.define_task(null, {name:'msec'});

You must also provide an .init() function:

  //
  // do any initialization necessary for the task type
  //
  MyTimeout.prototype.init = function () {
     // this.msec is filled in before calling
     // but we can do any other initialization here.
  };

When a task goes into wait state, it calls the .request() method of the class. This is where the task should initiate some work. When the task completes, the developer should call task.ready(result) or task.error(code, message, details) to notify other tasks.

  //
  // .request() is where the code to initiate the task goes
  //
  MyTimeout.prototype.request = function () {
     // task wrapper for window.setTimeout()
     var task = this;
     this.token = window.setTimeout(this.msec, function() {
         // when the timeout occurs, we go into ready state.
         task.ready(true);
     });
  };