Async library

BooJs includes a simple yet powerful asynchronous library modeled after the Promises/A CommonJS spec also known as thenables. You can read more about this asynchronous programming pattern on Wikipedia.

By supporting the Promises/A spec we ensure compatibility with some of the most popular JavaScript frameworks like Dojo or jQuery. Other frameworks implementing the deferred, promise, future or task patterns can be easily modified to be made compatible with Promises/A for most use cases.

The Async library is exposed as an optional namespace, not included by default by the compiler, which you can use in your own code just by importing the Async namespace and loading the Boo.Async.js file in your environment.

Deferred

The Deferred class allows to create, control and resolve promises which can be consumed by your own code or passed on to third party libraries that understand the Promises/A spec.

def make_async(v):
    # Create a new deferred
    defer = Deferred()
    # Launch the async job
    setTimeout({
        # Resolve the deferred with the final value
        defer.resolve(v)
    }, 1000)
    # Return the deferred promise which can be observed
    return defer.promise

# Obtain a promise
promise = make_async('foo')
# Register a callback to observe the successful resolution of the promise
promise.done({ x | print x })
# Register a callback to observe the wrongful resolution of the promise
promise.fail({ x | print 'Error:', x })

Note

Unlike some implementation that focus on raw performance (ie jQuery), Deferred works internally as a tree instead of a list. Each time you attach a callback to a deferred a new one is created internally returning its public interface, a Promise, this allows to model complex flows avoiding side effects.

Note

A common pitfall when using the promise pattern is that some errors might go unnoticed if we are not very careful to observe failures on every promise generated. To avoid this you can assign a global callback in Deferred.onError to act upon any rejected promise that isn’t explicitly controlled. It’s the equivalent to a handler for uncaught exceptions. By default, if no custom handler is assigned, a exception is raised with the reported error.

Promise

A Promise object is the public interface of a Deferred, there is a 1:1 relationship between them. While a Deferred allows to control its cancellation, rejection and resolution a Promise only allows to register our interest in its resolution (successful or not).

The Promises/A spec just specifies that a Promise object must expose a public method called then which receives 3 arguments: successCallback, failureCallback and progressCallback. This simple design makes it trivial to share promises between third party libraries, like jQuery for instance, allowing to observe the resolution of an asynchronous task with ease.

Utilities

enqueue

This simple method allows to defer the execution of a callback until the stack is empty. This is specially useful when you want to trigger an action just after a configuration step has completed. For instance, this method is used internally by Deferred to resolve them, that’s why they can be used also with immediate values.

d = {}
enqueue({ d.run() })
d.run = def():
  print 'Foo!'
# d.run() will called now

Note

In a browser environment it will use setImmediate or setTimeout with a timeout of 0. For Node it will use process.nextTick.

when

A common pattern is to wait until two or more action have completed before continuing. The when method will produce a Promise that gets resolved only when all the arguments given are resolved successfully. If any of them is rejected the Promise is rejected also and the other arguments are canceled.

p = when( jQuery.get('/data/a'), jQuery.get('/data/b'), 'immediate value' )

p.done def(results):
  a, b, immediate = results
  print a, b, immediate

p.fail def(error):
  print 'An error occurred fetching data:', error

Note

when can also be used as a shortcut to wrap any value in a promise which gets almost instantly resolved.

sleep

This method generates a Promise that gets resolved after the given milliseconds. It can be used to delay the execution of some code without blocking the execution thread.

p = sleep(10s)
p.done:
    print 'Woke up after 10 seconds'

# It also supports providing a callback directly
sleep 10s:
    print 'Woke up after 10 seconds'

Async/Await

One of the nicest features of the Async library is its implementation of the Async/Await pattern. Modeling your logic around promises is a nice way to support asynchronicity, however it forces you to replace the language native flow control mechanisms by those of the Deferred API. The Async/Await pattern removes that limitation, allowing you to write promise based code as if they were synchronous operations.

Under the hood the pattern makes use of coroutines (constructed via generators) to suspend and resume the execution of code at any point in a function based on the result of a Promise.

When we annotate a method as async we are telling the compiler that we want to control its execution in a special way, suspending it when an await keyword is found until its value is resolved. In other words, the await keyword indicates that we want to wait at that point until the given Promise object is resolved, avoiding the need to chain callbacks to control the program logic flow.

[async] def fetch(url):
    print "Fetching $url"
    try:
        # jQuery's ajax methods are Promises/A compatible
        await data = jQuery.get(url)
        print data
    except ex:
        print 'Error:', ex

The code above is roughly equivalent to the following one:

def fetch(url):
    print "Fetching $url"
    promise = jQuery.get(url)
    promise.done = def(data):
        print data
    promise.fail = def(error):
        print 'Error:', error

Even in this simple example the benefits of the Async/Await version are obvious. The complexity of using the promise API is hidden from us, with the added benefit that every async method always returns a promise itself, thus it’s very easy to compose complex flows with them.

def fetch(id):
    print 'Fetching data'
    await data = jQuery.get('http://ajax.com/' + id)
    return data

def update(id):
    await data = fetch(id)
    data.foo = 10
    await jQuery.put('http://ajax.com/' + id, data)
    print 'Data updated'

Another point where this pattern excels is in the handling of error conditions. There is no need to observe the promises for failures, using the native try/except mechanism we can control failures in a clean way, even maintaining a meaningful stacktrace to troubleshot any problem.

Note

The await keyword also works for multiple values, by using when under the hood. This means that we can easily parallelize asynchronous operations and only resume execution when all of them have completed.