Fork me on GitHub Mozilla

generators + promises = tasks

task.js is an experimental library for ES6 that makes sequential, blocking I/O simple and beautiful, using the power of JavaScript’s new yield operator.

Tasks are interleaved like threads, but they are cooperative rather than pre-emptive: they block on promises with yield. Here’s an example using jQuery:

spawn(function*() {
    var data = yield $.ajax(url);
    $('#result').html(data);
    var status = $('#status').html('Download complete.');
    yield status.fadeIn().promise();
    yield sleep(2000);
    status.fadeOut();
});

task.js works with any framework or library that uses the Promises/A spec.

Quick Start

The power of task.js comes from ES6 generators, which are currently only available in Firefox. You can try it out in Firefox, although there are a few remaining incompatibilities with the current ES6 spec (which is still a work in progress).

Here’s a “hello world” of tasks that will work in Firefox:

<script type="application/javascript" src="task.js"></script>

<!-- 'yield' and 'let' keywords require version opt-in -->
<script type="application/javascript;version=1.8">
function hello() {
    let { spawn, sleep } = task;
    spawn(function() {      // Firefox does not yet use the function* syntax
        alert("Hello...");
        yield sleep(1000);
        alert("...world!");
    });
}
</script>

The spawn function takes a generator function and starts running it as a concurrently-executed task. Every time the task yields a promise, the scheduler blocks the task until the promise is fulfilled.

Tasks
function spawn( f ) → Task

Creates a new task and automatically starts it.

f() → generator

A function to produce the generator for running the task. The function is called with this bound to the new task.

class Task< A > is promise< A >

A concurrent and suspendable computation.

[new] Task( f )
f() → generator

A function to produce the generator for running the task. The function is called with this bound to the new task.

Task.current() → Task | null

Returns the currently executing task, if any.

t.start() → t

Starts the task. If the task is blocked on a promise, the task will not resume executing until the promise is fulfilled. Otherwise, the scheduler will resume the task at some point.

t.pause() → t

Pauses the task. Even when the task is ready to resume, it will not resume until it is started again. If the task is waiting on a promise that becomes fulfilled while the task is paused, its result will not be lost; it will be returned whenever the task is started again.

A task cannot be paused if it is currently running. To pause a running task after it reaches its next yield-point, enqueue a callback to pause it in a different turn of the event loop:

enqueue(function() {
    task.pause();
});
t.isStarted() → boolean

Indicates whether the task is started.

t.isRunning() → boolean

Indicates whether the task is currently executing.

inherits from Promise< A >

get, put, call, addCallback, addErrback, addBoth, addCallbacks, timeout

interface generator

An object encapsulating a suspendable computation. In ES6, generators can be conveniently created using the yield operator.

g.send( result ) → promise< any > | falsey

Resumes the computation to its next yield point or completion, whichever comes first. The computation should yield either a promise, indicating that the computation is blocked until the promise is fulfilled, or a falsey value, indicating that the computation is immediately ready to resume.

result : any

The result to resume the previous yield with.

g.throw( error ) → promise< any > | falsey

Resumes the computation by throwing an error. The computation runs to its next yield point or completion, whichever comes first. The computation should yield either a promise, indicating that the computation is blocked until the promise is fulfilled, or a falsey value, indicating that the computation is immediately ready to resume.

error : any

The error to throw in the resumed computation.

g.close() → any

Cancels the computation, executing any live finally blocks remaining in the generator. Closing the computation should not expect to be able to return any more promises; they will be ignored by the scheduler, which will consider the computation to be terminated.

class TaskResult< A >

ES6 allows generator functions to return a value, but the Firefox JavaScript engine (the only that currently implements generators) does not yet allow this. To produce a result value from a task, throw an instance of this class.

The task result value.

Promises
interface promise< A >

An object representing an “eventual value,” i.e., a value that may not be ready yet but will be provided at some point in the future.

p.then( [ onResolve [, onError [, onProgress ] ] ] ) → promise< B >

Registers callbacks to be called by the promise.

onResolve( result ) → B

Callback to be called by the promise when it produces its result.

result : A The result value produced by the promise.

onError( error ) → B

Callback to be called by the promise when it throws an error while trying to produce its result.

error : any The error thrown while attempting to produce a result.

onProgress() → any

Callback to be called by the promise while making progress towards producing its result.

p.cancel() → any

Cancel the retrieval of the promise’s value. If the promise is already fulfilled, this operation does nothing.

function now( value ) → promise< A >

Converts an ordinary value into a fulfilled promise.

value : any

The promise’s resolved value.

function never() → promise< any >

Produces a promise that is never fulfilled.

function join( promise ... ) → promise< [A] >

Produces a promise that is resolved when all the given promises are resolved. The resolved value is an array of each of the resolved values of the given promises.

If any of the promises is rejected, the joined promise is rejected with the same error, and any remaining unfulfilled promises are cancelled.

promise : promise< A >

A promise to join.

function choose( promise ... ) → promise< A >

Produces a promise that is fulfilled when any one of the given promises is fulfilled. As soon as one of the promises is fulfilled, whether by being resolved or rejected, all the other promises are cancelled.

promise : promise< A >

A promise to choose from.

function sleep( delay[, compensate=false ] ) → promise< uint32 >

Produces a promise that is resolved when a given amount of time elapses, using the builtin global setTimeout function.

The promise’s resolved value is the actual number of milliseconds elapsed since the promise was created.

delay : uint32

The number of milliseconds to sleep for.

compensate : boolish

Indicates whether to compensate for imprecision in the host platform by continuing to sleep if the timeout completes before the indicated number of milliseconds have elapsed.

Scheduling
interface scheduler

An object providing a scheduling policy. A scheduler maintains an internal collection of scheduled tasks and decides which task to choose next for execution.

s.choose() → Task

Chooses a scheduled task for the runtime to execute next, removing it from the internal collection of scheduled tasks.

s.schedule( task ) → any

Adds a task to the scheduler’s internal collection of scheduled tasks. The task is guaranteed not to be currently scheduled.

task : Task

The task to schedule.

s.unschedule( task ) → any

Removes a task from the scheduler’s internal collection of scheduled tasks. The task may or may not already be scheduled.

task : Task

The task to unschedule.

function currentScheduler() → scheduler

Returns the currently-installed system scheduler.

function setCurrentScheduler( scheduler ) → void

Installs a new system scheduler. Any existing tasks will continue to be scheduled via the scheduler they were created with, but new tasks will be associated with the new scheduler.

scheduler : scheduler

The new scheduler.

class RandomScheduler is scheduler

A scheduler that chooses tasks randomly using Math.random.

function enqueue( f ) → void

Calls the given function in a separate turn of the event loop.

f() → any

The function to call in another event loop turn.

Internal
class Deferred< A > is promise< A >

A low-level promise that can be manually fulfilled.

[new] Deferred( [ cancel ] )
d.resolve( result ) → void
d.reject( error ) → void
inherits from Promise< A >

get, put, call, addCallback, addErrback, addBoth, addCallbacks, timeout

class Promise< A >

A partial implementation of a promise, providing utility methods common in popular promise implementations. To implement the full promise API, instances must be given then and cancel methods.

[new] Promise()
p.get( key )
p.put( key, val )
p.call( key, ...args )
p.addCallback( callback )
p.addErrback( errback )
p.addBoth( callback )
p.addCallbacks( callback, errback )
p.timeout( delay [, compensate=false ] )