Q:

Grails asynchronous Promise / for loop error - how to pass variable to task closure

I have a number of files that need to be parsed so I am doing this over several threads.
int fileCount = 16
def promiseList = []
for (int i = 1; i <= fileCount; i++) {
    println i
    def p = task {
        println "${new Date()} Starting parse of schedules (${i})..."
        // do some parsing here, where I need access to the value i     
    }
    p.onError {Throwable t ->
        println "Serious error when loading schedule ${i}, ${t.getMessage()}"
        t.printStackTrace()
    }
    promiseList << p
}
waitAll(promiseList)

The idea here is to create multiple promises, set them all running asynchronously then way for all of them to complete.
Wed Feb 25 16:36:36 GMT 2015 Starting parse of schedules (1)...
Wed Feb 25 16:36:36 GMT 2015 Starting parse of schedules (3)...
Wed Feb 25 16:36:36 GMT 2015 Starting parse of schedules (4)...
Wed Feb 25 16:36:36 GMT 2015 Starting parse of schedules (5)...
Wed Feb 25 16:36:36 GMT 2015 Starting parse of schedules (6)...
Wed Feb 25 16:36:36 GMT 2015 Starting parse of schedules (7)...
Wed Feb 25 16:36:36 GMT 2015 Starting parse of schedules (8)...
Wed Feb 25 16:36:36 GMT 2015 Starting parse of schedules (9)...
Wed Feb 25 16:36:36 GMT 2015 Starting parse of schedules (10)...
Wed Feb 25 16:36:36 GMT 2015 Starting parse of schedules (11)...
Wed Feb 25 16:36:36 GMT 2015 Starting parse of schedules (13)...
Wed Feb 25 16:36:36 GMT 2015 Starting parse of schedules (12)...
Wed Feb 25 16:36:36 GMT 2015 Starting parse of schedules (14)...
Wed Feb 25 16:36:36 GMT 2015 Starting parse of schedules (15)...
Wed Feb 25 16:36:36 GMT 2015 Starting parse of schedules (16)...
Wed Feb 25 16:36:36 GMT 2015 Starting parse of schedules (17)...

Question: what on Earth happened to task number 2?  If I put:
println i

into the for loop, then all is as expected (i.e. I get 1..16).  I think the variable is being incremented and then at some later point the closure looks at the variable and it's already been incremented, hence the incorrect values.  How can I pass the value i to the closure so that I get the correct values?
EDIT: For what it's worth, I can hack it, which I really don't like doing, by inserting:
// for ()
    int taskNumber = i
    // create task here, refer to local variable taskNumber instead of i
    Thread.currentThread().sleep((long)(1000))

at the end of the for loop, so that the task is properly instantiated before the next loop occurs.  However, this is an exceptionally nasty hack and I'm sure there must be a better way of passing a variable to a thread?
Further edit:
Here is the output when I implement the for loop using:
for (i in 1..fileCount)

Wed Feb 25 16:57:54 GMT 2015 Starting parse of schedules (1)...
Wed Feb 25 16:57:54 GMT 2015 Starting parse of schedules (3)...
Wed Feb 25 16:57:54 GMT 2015 Starting parse of schedules (4)...
Wed Feb 25 16:57:54 GMT 2015 Starting parse of schedules (5)...
Wed Feb 25 16:57:54 GMT 2015 Starting parse of schedules (6)...
Wed Feb 25 16:57:54 GMT 2015 Starting parse of schedules (8)...
Wed Feb 25 16:57:54 GMT 2015 Starting parse of schedules (7)...
Wed Feb 25 16:57:54 GMT 2015 Starting parse of schedules (9)...
Wed Feb 25 16:57:54 GMT 2015 Starting parse of schedules (11)...
Wed Feb 25 16:57:54 GMT 2015 Starting parse of schedules (13)...
Wed Feb 25 16:57:54 GMT 2015 Starting parse of schedules (13)...
Wed Feb 25 16:57:54 GMT 2015 Starting parse of schedules (13)...
Wed Feb 25 16:57:54 GMT 2015 Starting parse of schedules (14)...
Wed Feb 25 16:57:54 GMT 2015 Starting parse of schedules (15)...
Wed Feb 25 16:57:54 GMT 2015 Starting parse of schedules (16)...
Wed Feb 25 16:57:54 GMT 2015 Starting parse of schedules (16)...

A:

i is shared between all your closures/tasks currently. You need to pass copy of i from current context/current thread into closure itself, during closure creation. You can do it by using currying, like:
def p = task({int locali -> 
    println "${new Date()} Starting parse of schedules (${locali})..."
    // do some parsing here, where I need access to the value i     
}.curry(i))
p.onError({int locali, Throwable t ->
    println "Serious error when loading schedule ${locali}, ${t.getMessage()}"
    t.printStackTrace()
}.curry(i))

Another way will be create closure from different context (more Java-way, and basically it's what you did with collect hack):
Closure createTask(final int i) {
  return {
    println "${new Date()} Starting parse of schedules (${i})..."
    // do some parsing here, where I need access to the value i     
  }
}

then:
def p = task(createTask(i))

A:

I have a solution that appears to work , but I'm not sure if this is due to chance or not.
EDIT:  If I add a sleep() to the task, as suggested by @cfrick in the comments to the question and as shown below, then the items are all processed (although the order is random).
The for loop should be replaced by collect:
(1..fileCount).collect { i->
    def p = task {
        // this sleep() call is added to check if all tasks are run with the correct parameters
        Thread.currentThread().sleep((long)(1000))
        int taskNumber = i
        println "${new Date()} Starting parse of schedules (${taskNumber})..."
    }
    p.onError {Throwable t ->
        int taskNumber = i
        println "Serious error when loading schedule ${taskNumber}, ${t.getMessage()}"
        t.printStackTrace()
    }
    promiseList << p
}
waitAll(promiseList)

So, following the suggestion from @cfrick, the code could be modified to be simpler still:
promiseList = (1..fileCount).collect { i->
    def p = task {
        println "${new Date()} Starting parse of schedules (${i})..."
        // do work here
    }
    p.onError {Throwable t ->
        println "Serious error when loading schedule ${i}, ${t.getMessage()}"
        t.printStackTrace()
    }
    return p
}
waitAll(promiseList)

