Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
A simple library written in CoffeeScript for delaying operations until asynchronous calls have completed.
CoffeeScript
branch: master

Fetching latest commit…

Cannot retrieve the latest commit at this time

Failed to load latest commit information.
DelayedOp.coffee
MIT-License.txt
README.md
demo.coffee
demo.html

README.md

Table of Contents

        What is this for?
        Getting the library
        Usage
                 Creating the op object
                 Adding operations
                         Simple Style
                         Callback Style
                         Return Style
                 Tags
                 Finishing Up
                 Debugging

What is this for?

When working with asynchronous functions (e.g. AJAX), it is often the case that you want to make several asynchronous calls at once, and then have some callback execute once all of those asynchronous operations have finished.

DelayedOp is a micro-library written in CoffeeScript that simplifies this process while making it easy to debug when things go wrong.

Take a look at demo.coffee if you want to jump right in, or read on for a more detailed discussion.

Getting the library

You can compile from the original DelayedOp.coffee source, or download the latest compiled and minified version of the library.

Usage

Creating the op object

To begin with, you simply create a DelayedOp (you can give it an optional name for debugging purposes)

/* JavaScript */
var op = new DelayedOp('My Operation');
### CoffeeScript ###
op = new DelayedOp 'My Operation'

Adding operations

There are three different styles for using this new object, and each style can be either tagged or untagged.

Simple Style

/* JavaScript */
op.wait();
someAsyncFunction(function() {
    op.ok();
});
### CoffeeScript ###
op.wait()
someAsyncFunction ->
    ### Do stuff ###
    op.ok()

This style is the easiest to use. You call op.wait() before the async call, and then in the async function's callback, you call op.ok().

For simple use cases, this call style should be fine, but it will not help you much if you make an error. If op.ok() is called too many times, it will throw an exception, but you'll have to hunt down the extra call yourself.

Wouldn't it be nice if DelayedOp could do some tracking for us in case we make a mistake? Well...

Callback Style

/* JavaScript */
op.wait(function(ok) {
    someAsyncFunction(function() {
        /* Do stuff */
        ok();
    });
}
### CoffeeScript ###
op.wait (ok) -> someAsyncFunction ->
    ### Do stuff ###
    ok()

In this style, we pass a callback to wait which accepts an ok function as its parameter. The ok function DelayedOp passes to the callback will throw an exception if it's called more than once. If you make a programming error, you'll know specifically where the problem is.

The only disadvantage to this style is that it adds a bit more boilerplate, especially in plain JavaScript.

Return Style

/* JavaScript */
var ok = op.wait();
someAsyncFunction(function() {
    /* Do stuff */
    ok();
})
### CoffeeScript ###
ok = op.wait()
someAsyncFunction ->
    ### Do stuff ###
    ok()

The wait function returns a special ok function (the same one passed to the callback in the callback style), which can only be called once (it throws an error if you call it twice). One particularly useful pattern using this style when no additional processing is needed in the async callback is like so:

/* JavaScript */
someAsyncFunction(op.wait());
### CoffeeScript ###
someAsyncFunction op.wait()

In many cases, however, you will find that this style requires you to pollute the current scope with variables to hold your ok functions. In such cases, the callback style is usually more appropriate.

Tags

You can pass a string "tag" to the wait method. This aids in tracking down errors if ok calls outweigh your wait calls, and it can also help you to find the inverse problem (see "Debugging" below).

To attach a tag, pass it as the first parameter to wait, and pass the same tag to the corresponding call to ok, like so:

/* JavaScript */
op.wait('SomeTag');
someAsyncFunction(function() {
    op.ok('SomeTag');
});
### CoffeeScript ###
op.wait 'SomeTag'
someAsyncFunction ->
    ### Do stuff ###
    op.ok 'SomeTag'

If your calls to ok exceed those to wait for any given tag, an error will be thrown, giving you a better idea of where the problem is.

You can also pass tags to the other call styles:

/* JavaScript */

// Callback style
op.wait('SomeTag', function(ok) {
    someAsyncFunction(function() {
        /* Do stuff */
        ok(); 
    });
}

// Return style
var ok = op.wait('AnotherTag');
someAsyncFunction(function() {
    /* Do stuff */
    ok();
})
### CoffeeScript ###

# Callback style
op.wait 'SomeTag', (ok) -> someAsyncFunction ->
    ### Do stuff ###
    ok()

# Return style
ok = op.wait 'AnotherTag'
someAsyncFunction ->
    ### Do stuff ###
    ok()

Note that for the callback and return styles, you don't have to pass the tag to the ok function. When creating the single-use ok function, DelayedOp automatically curries the tag parameter for you.

Finishing up

Once you've set up the operation, you use the ready method to give it a callback. This callback will run once all wait calls have been balanced by ok calls

/* JavaScript */
op.ready(function(){
    alert('All operations are complete.');
});
### CoffeeScript ###
op.ready -> alert 'All operations are complete.'

Debugging

DelayedOp throws informative exceptions if you have too many ok calls, or too few waits, but what if you forget to call ok or accidentally insert a duplicate wait?

When you create the operation, you can pass it a delay in seconds for the maximum time you expect your asynchronous calls to take, like so:

/* JavaScript */
var op = new DelayedOp('My Operation', 10);
### CoffeeScript ###
op = new DelayedOp 'My Operation', 10

This will tell the operation to wait 10 seconds after ready() is called, and then log its name and unbalanced tags to the console if it has not yet fired. The output will look like this:

DelayedOp Timeout: My Operation
    foo: 2
    bar: 1

You can also see a list of all unfired operations and their unbalanced tags like so:

DelayedOp.logPending()
Something went wrong with that request. Please try again.