Version 9 of promise

Updated 2016-04-03 15:12:12 by pooryorick

2016-01-26: promise 1.0a1 released

Promises are concurrency primitives that let you write asynchronous code in a sequential style. The promise package is a Tcl based implementation of promises modeled for the most part on the Javascript/ECMAScript 6 standard.

Project page and downloads are at http://sourceforge.net/projects/tcl-promise/ .

Reference documentation is at http://tcl-promise.sourceforge.net but it's probably best to start with the posts at http://www.magicsplat.com/blog/tags/promises/ for an introduction with examples.

APN In response to a question on the chat about running multiple du programs in parallel, here is a promise based solution. Besides printing the output of each du invocation, it also prints the total disk usage for the given paths once all invocations exit.

First define the procedures to handle successful and failed (for example, non-existent path) completions.

proc on_success {du_output} {
    puts $du_output
    # Assumes a very specific format for du output and return the used space.
    return [scan $du_output %d]
}

proc on_failure {message error_dictionary} {
    puts $message
    # On errors, return 0 as used space after printing the error
    return 0
}

Assume paths contains the list of paths of interest.

set paths [list c:/Tcl c:/Temp c:/nosuchpathexists]

Create a promise for each invocation of du, passing it the commands to call on successful and unsuccessful completions.

set promises [lmap path $paths {
    set promise [promise::pexec du -sk $path]
    $promise then on_success on_failure
}]

Finally, combine all the promises into one which will calculate the total once all promises are fulfilled.

set totaller [promise::all $promises]
$totaller done [promise::lambda {outputs} {
    puts "Total: [tcl::mathop::+ {*}$outputs]"
}]

Note that as always, promises require the Tcl event loop to be running.

The following output is produced:

/usr/bin/du: cannot access `c:/nosuchpathexists': No such file or directory
180149        c:/Temp

229933        c:/Tcl

Total: 410082

PYK 2015-04-02: Promises are primarily useful as a stop-gap until a language grows real coroutines. Fortunately, Tcl already has coroutines. For comparison here's a coroutine implementation of the example:

#! /bin/env tclsh

proc du_multi {varname args} {
    upvar 1 $varname vname
    set chans {}
    foreach arg $args {
        set chan [open |[list du -sk $arg]]
        chan configure $chan -blocking 0
        chan event $chan readable [list [info coroutine] [list $arg $chan]]
        dict set chans $chan {}
    }
    while {[llength [dict keys $chans]]} {
        lassign [yield] dirname chan
        if {[eof $chan]} {
            set dirsize [scan [dict get $data $chan] %d]
            puts [list $dirname $dirsize]
            incr total $dirsize
            close $chan
            dict unset chans $chan
        } else {
            dict append data $chan [read $chan]
        }
    }
    set vname $total
}


proc main {argv0 argv} {
    variable total
    coroutine dm du_multi total {*}$argv
    vwait total
    puts [string repeat _ [string length "total $total"]]
    puts [list total $total]
}

main $argv0 $argv

APN Humbly begs to differ. I see coroutines as one way that promises could be implemented in a language. Async callbacks via the event loop is another. What I found most useful about promises is the "contracts" they define that allows them to be combined in various ways (error handling also being a big part of that). Could you do something similar with coroutines? Of course you could but you would have simply reimplemented a promise library on top of coroutines.

PYK 2016-04-03: I'm just really curious about all the noise around promises and futures. I keep reading about them, but each example I've seen looks to me like it would have been better-written as a coroutine. Maybe eventually this page will sport some code that shows something that looks better with promises than as coroutines. I'll also hazard a prediction that once coroutines really get rolling in Javascript, promises will fade away.